Terminal implements important half of search protocol
This commit is contained in:
parent
63d9d29762
commit
3f11fd3b8b
8 changed files with 249 additions and 119 deletions
|
@ -424,8 +424,7 @@
|
|||
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
|
||||
"cmd-c": "terminal::Copy",
|
||||
"cmd-v": "terminal::Paste",
|
||||
"cmd-k": "terminal::Clear",
|
||||
"cmd-s": "terminal::SearchTest"
|
||||
"cmd-k": "terminal::Clear"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -513,17 +513,17 @@ impl SearchableItem for Editor {
|
|||
|
||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
|
||||
match event {
|
||||
Event::BufferEdited => Some(SearchEvent::ContentsUpdated),
|
||||
Event::SelectionsChanged { .. } => Some(SearchEvent::SelectionsChanged),
|
||||
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
|
||||
Event::SelectionsChanged { .. } => Some(SearchEvent::ActiveMatchChanged),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_highlights(&mut self, cx: &mut ViewContext<Self>) {
|
||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.clear_background_highlights::<BufferSearchHighlights>(cx);
|
||||
}
|
||||
|
||||
fn highlight_matches(&mut self, matches: Vec<Range<Anchor>>, cx: &mut ViewContext<Self>) {
|
||||
fn update_matches(&mut self, matches: Vec<Range<Anchor>>, cx: &mut ViewContext<Self>) {
|
||||
self.highlight_background::<BufferSearchHighlights>(
|
||||
matches,
|
||||
|theme| theme.search.match_background,
|
||||
|
@ -553,7 +553,7 @@ impl SearchableItem for Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn select_next_match_in_direction(
|
||||
fn activate_next_match(
|
||||
&mut self,
|
||||
index: usize,
|
||||
direction: Direction,
|
||||
|
@ -575,7 +575,7 @@ impl SearchableItem for Editor {
|
|||
});
|
||||
}
|
||||
|
||||
fn select_match_by_index(
|
||||
fn activate_match_at_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
matches: Vec<Range<Anchor>>,
|
||||
|
@ -586,7 +586,7 @@ impl SearchableItem for Editor {
|
|||
});
|
||||
}
|
||||
|
||||
fn matches(
|
||||
fn find_matches(
|
||||
&mut self,
|
||||
query: project::search::SearchQuery,
|
||||
cx: &mut ViewContext<Self>,
|
||||
|
|
|
@ -174,7 +174,9 @@ impl ToolbarItemView for BufferSearchBar {
|
|||
cx,
|
||||
Box::new(move |search_event, cx| {
|
||||
if let Some(this) = handle.upgrade(cx) {
|
||||
this.update(cx, |this, cx| this.on_active_editor_event(search_event, cx));
|
||||
this.update(cx, |this, cx| {
|
||||
this.on_active_searchable_item_event(search_event, cx)
|
||||
});
|
||||
}
|
||||
}),
|
||||
));
|
||||
|
@ -461,10 +463,10 @@ impl BufferSearchBar {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_active_editor_event(&mut self, event: SearchEvent, cx: &mut ViewContext<Self>) {
|
||||
fn on_active_searchable_item_event(&mut self, event: SearchEvent, cx: &mut ViewContext<Self>) {
|
||||
match event {
|
||||
SearchEvent::ContentsUpdated => self.update_matches(false, cx),
|
||||
SearchEvent::SelectionsChanged => self.update_match_index(cx),
|
||||
SearchEvent::MatchesInvalidated => self.update_matches(false, cx),
|
||||
SearchEvent::ActiveMatchChanged => self.update_match_index(cx),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
|
|||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
fmt::Display,
|
||||
ops::Sub,
|
||||
ops::{RangeInclusive, Sub},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
|
@ -48,7 +48,7 @@ use gpui::{
|
|||
keymap::Keystroke,
|
||||
scene::{ClickRegionEvent, DownRegionEvent, DragRegionEvent, UpRegionEvent},
|
||||
ClipboardItem, Entity, ModelContext, MouseButton, MouseMovedEvent, MutableAppContext,
|
||||
ScrollWheelEvent,
|
||||
ScrollWheelEvent, Task,
|
||||
};
|
||||
|
||||
use crate::mappings::{
|
||||
|
@ -68,8 +68,6 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
///Scroll multiplier that is set to 3 by default. This will be removed when I
|
||||
///Implement scroll bars.
|
||||
const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
|
||||
// const ALACRITTY_SEARCH_LINE_LIMIT: usize = 1000;
|
||||
const SEARCH_FORWARD: Direction = Direction::Left;
|
||||
const MAX_SEARCH_LINES: usize = 100;
|
||||
const DEBUG_TERMINAL_WIDTH: f32 = 500.;
|
||||
const DEBUG_TERMINAL_HEIGHT: f32 = 30.;
|
||||
|
@ -91,7 +89,7 @@ enum InternalEvent {
|
|||
ColorRequest(usize, Arc<dyn Fn(Rgb) -> String + Sync + Send + 'static>),
|
||||
Resize(TerminalSize),
|
||||
Clear,
|
||||
FocusNextMatch,
|
||||
// FocusNextMatch,
|
||||
Scroll(AlacScroll),
|
||||
SetSelection(Option<Selection>),
|
||||
UpdateSelection(Vector2F),
|
||||
|
@ -382,7 +380,8 @@ impl TerminalBuilder {
|
|||
cur_size: initial_size,
|
||||
last_mouse: None,
|
||||
last_offset: 0,
|
||||
searcher: None,
|
||||
matches: Vec::new(),
|
||||
selection_text: None,
|
||||
};
|
||||
|
||||
Ok(TerminalBuilder {
|
||||
|
@ -454,7 +453,8 @@ pub struct Terminal {
|
|||
last_mode: TermMode,
|
||||
last_offset: usize,
|
||||
last_mouse: Option<(Point, Direction)>,
|
||||
searcher: Option<(Option<RegexSearch>, Point)>,
|
||||
pub matches: Vec<RangeInclusive<Point>>,
|
||||
pub selection_text: Option<String>,
|
||||
}
|
||||
|
||||
impl Terminal {
|
||||
|
@ -531,32 +531,32 @@ impl Terminal {
|
|||
InternalEvent::Scroll(scroll) => {
|
||||
term.scroll_display(*scroll);
|
||||
}
|
||||
InternalEvent::FocusNextMatch => {
|
||||
if let Some((Some(searcher), _origin)) = &self.searcher {
|
||||
match term.search_next(
|
||||
searcher,
|
||||
Point {
|
||||
line: Line(0),
|
||||
column: Column(0),
|
||||
},
|
||||
SEARCH_FORWARD,
|
||||
Direction::Left,
|
||||
None,
|
||||
) {
|
||||
Some(regex_match) => {
|
||||
term.scroll_to_point(*regex_match.start());
|
||||
// InternalEvent::FocusNextMatch => {
|
||||
// if let Some((Some(searcher), _origin)) = &self.searcher {
|
||||
// match term.search_next(
|
||||
// searcher,
|
||||
// Point {
|
||||
// line: Line(0),
|
||||
// column: Column(0),
|
||||
// },
|
||||
// SEARCH_FORWARD,
|
||||
// Direction::Left,
|
||||
// None,
|
||||
// ) {
|
||||
// Some(regex_match) => {
|
||||
// term.scroll_to_point(*regex_match.start());
|
||||
|
||||
//Focus is done with selections in zed
|
||||
let focus = make_selection(*regex_match.start(), *regex_match.end());
|
||||
term.selection = Some(focus);
|
||||
}
|
||||
None => {
|
||||
//Clear focused match
|
||||
term.selection = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// //Focus is done with selections in zed
|
||||
// let focus = make_selection(*regex_match.start(), *regex_match.end());
|
||||
// term.selection = Some(focus);
|
||||
// }
|
||||
// None => {
|
||||
// //Clear focused match
|
||||
// term.selection = None;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
InternalEvent::SetSelection(sel) => term.selection = sel.clone(),
|
||||
InternalEvent::UpdateSelection(position) => {
|
||||
if let Some(mut selection) = term.selection.take() {
|
||||
|
@ -594,34 +594,34 @@ impl Terminal {
|
|||
self.events.push_back(InternalEvent::Scroll(scroll));
|
||||
}
|
||||
|
||||
fn focus_next_match(&mut self) {
|
||||
self.events.push_back(InternalEvent::FocusNextMatch);
|
||||
}
|
||||
// fn focus_next_match(&mut self) {
|
||||
// self.events.push_back(InternalEvent::FocusNextMatch);
|
||||
// }
|
||||
|
||||
pub fn search(&mut self, search: &str) {
|
||||
let new_searcher = RegexSearch::new(search).ok();
|
||||
self.searcher = match (new_searcher, &self.searcher) {
|
||||
//Nothing to do :(
|
||||
(None, None) => None,
|
||||
//No existing search, start a new one
|
||||
(Some(new_searcher), None) => Some((Some(new_searcher), self.viewport_origin())),
|
||||
//Existing search, carry over origin
|
||||
(new_searcher, Some((_, origin))) => Some((new_searcher, *origin)),
|
||||
};
|
||||
// pub fn search(&mut self, search: &str) {
|
||||
// let new_searcher = RegexSearch::new(search).ok();
|
||||
// self.searcher = match (new_searcher, &self.searcher) {
|
||||
// //Nothing to do :(
|
||||
// (None, None) => None,
|
||||
// //No existing search, start a new one
|
||||
// (Some(new_searcher), None) => Some((Some(new_searcher), self.viewport_origin())),
|
||||
// //Existing search, carry over origin
|
||||
// (new_searcher, Some((_, origin))) => Some((new_searcher, *origin)),
|
||||
// };
|
||||
|
||||
if let Some((Some(_), _)) = self.searcher {
|
||||
self.focus_next_match();
|
||||
}
|
||||
}
|
||||
// if let Some((Some(_), _)) = self.searcher {
|
||||
// self.focus_next_match();
|
||||
// }
|
||||
// }
|
||||
|
||||
fn viewport_origin(&mut self) -> Point {
|
||||
let viewport_top = alacritty_terminal::index::Line(-(self.last_offset as i32)) - 1;
|
||||
Point::new(viewport_top, alacritty_terminal::index::Column(0))
|
||||
}
|
||||
// fn viewport_origin(&mut self) -> Point {
|
||||
// let viewport_top = alacritty_terminal::index::Line(-(self.last_offset as i32)) - 1;
|
||||
// Point::new(viewport_top, alacritty_terminal::index::Column(0))
|
||||
// }
|
||||
|
||||
pub fn end_search(&mut self) {
|
||||
self.searcher = None;
|
||||
}
|
||||
// pub fn end_search(&mut self) {
|
||||
// self.searcher = None;
|
||||
// }
|
||||
|
||||
pub fn copy(&mut self) {
|
||||
self.events.push_back(InternalEvent::Copy);
|
||||
|
@ -669,12 +669,12 @@ impl Terminal {
|
|||
|
||||
pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
|
||||
where
|
||||
F: FnOnce(RenderableContent, char, Vec<Match>) -> T,
|
||||
F: FnOnce(RenderableContent, char) -> T,
|
||||
{
|
||||
let m = self.term.clone(); //Arc clone
|
||||
let mut term = m.lock();
|
||||
let term = self.term.clone();
|
||||
let mut term = term.lock();
|
||||
|
||||
//Note that this ordering matters for
|
||||
//Note that this ordering matters for event processing
|
||||
while let Some(e) = self.events.pop_front() {
|
||||
self.process_terminal_event(&e, &mut term, cx)
|
||||
}
|
||||
|
@ -683,16 +683,12 @@ impl Terminal {
|
|||
|
||||
let content = term.renderable_content();
|
||||
|
||||
self.selection_text = term.selection_to_string();
|
||||
self.last_offset = content.display_offset;
|
||||
|
||||
let cursor_text = term.grid()[content.cursor.point].c;
|
||||
|
||||
let mut matches = vec![];
|
||||
if let Some((Some(r), _)) = &self.searcher {
|
||||
matches.extend(make_search_matches(&term, &r));
|
||||
}
|
||||
|
||||
f(content, cursor_text, matches)
|
||||
f(content, cursor_text)
|
||||
}
|
||||
|
||||
pub fn focus_in(&self) {
|
||||
|
@ -865,6 +861,33 @@ impl Terminal {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_matches(
|
||||
&mut self,
|
||||
query: project::search::SearchQuery,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Vec<RangeInclusive<Point>>> {
|
||||
let term = self.term.clone();
|
||||
dbg!("Spawning find_matches");
|
||||
cx.background().spawn(async move {
|
||||
let searcher = match query {
|
||||
project::search::SearchQuery::Text { query, .. } => {
|
||||
RegexSearch::new(query.as_ref())
|
||||
}
|
||||
project::search::SearchQuery::Regex { query, .. } => {
|
||||
RegexSearch::new(query.as_ref())
|
||||
}
|
||||
};
|
||||
|
||||
if searcher.is_err() {
|
||||
return Vec::new();
|
||||
}
|
||||
let searcher = searcher.unwrap();
|
||||
|
||||
let term = term.lock();
|
||||
dbg!(make_search_matches(&term, &searcher).collect())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Terminal {
|
||||
|
@ -877,11 +900,11 @@ impl Entity for Terminal {
|
|||
type Event = Event;
|
||||
}
|
||||
|
||||
fn make_selection(from: Point, to: Point) -> Selection {
|
||||
let mut focus = Selection::new(SelectionType::Simple, from, Direction::Left);
|
||||
focus.update(to, Direction::Right);
|
||||
focus
|
||||
}
|
||||
// fn make_selection(from: Point, to: Point) -> Selection {
|
||||
// let mut focus = Selection::new(SelectionType::Simple, from, Direction::Left);
|
||||
// focus.update(to, Direction::Right);
|
||||
// focus
|
||||
// }
|
||||
|
||||
/// Copied from alacritty/src/display/hint.rs HintMatches::visible_regex_matches()
|
||||
/// Iterate over all visible regex matches.
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
use crate::terminal_view::TerminalView;
|
||||
use crate::{Event, Terminal, TerminalBuilder, TerminalError};
|
||||
|
||||
use alacritty_terminal::index::Point;
|
||||
use dirs::home_dir;
|
||||
use gpui::{
|
||||
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, View,
|
||||
ViewContext, ViewHandle,
|
||||
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, Task,
|
||||
View, ViewContext, ViewHandle,
|
||||
};
|
||||
use workspace::searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle};
|
||||
use workspace::{Item, Workspace};
|
||||
|
||||
use crate::TerminalSize;
|
||||
use project::{LocalWorktree, Project, ProjectPath};
|
||||
use settings::{AlternateScroll, Settings, WorkingDirectory};
|
||||
use smallvec::SmallVec;
|
||||
use std::ops::RangeInclusive;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::terminal_element::TerminalElement;
|
||||
|
@ -328,6 +331,98 @@ impl Item for TerminalContainer {
|
|||
fn should_close_item_on_event(event: &Self::Event) -> bool {
|
||||
matches!(event, &Event::CloseTerminal)
|
||||
}
|
||||
|
||||
fn as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
|
||||
Some(Box::new(handle.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl SearchableItem for TerminalContainer {
|
||||
type Match = RangeInclusive<Point>;
|
||||
|
||||
/// Convert events raised by this item into search-relevant events (if applicable)
|
||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
|
||||
match event {
|
||||
Event::Wakeup => Some(SearchEvent::MatchesInvalidated),
|
||||
//TODO selection changed
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear stored matches
|
||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let TerminalContent::Connected(connected) = &self.content {
|
||||
let terminal = connected.read(cx).terminal().clone();
|
||||
terminal.update(cx, |term, _| term.matches.clear())
|
||||
}
|
||||
}
|
||||
|
||||
/// Store matches returned from find_matches somewhere for rendering
|
||||
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
|
||||
if let TerminalContent::Connected(connected) = &self.content {
|
||||
let terminal = connected.read(cx).terminal().clone();
|
||||
dbg!(&matches);
|
||||
terminal.update(cx, |term, _| term.matches = matches)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the selection content to pre-load into this search
|
||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
||||
if let TerminalContent::Connected(connected) = &self.content {
|
||||
let terminal = connected.read(cx).terminal().clone();
|
||||
terminal.read(cx).selection_text.clone().unwrap_or_default()
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an index, a set of matches for this index, and a direction,
|
||||
/// get the next match (clicking the arrow)
|
||||
fn activate_next_match(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
_direction: Direction,
|
||||
_matches: Vec<Self::Match>,
|
||||
_cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
/// Focus match at given index into the Vec of matches
|
||||
fn activate_match_at_index(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
_matches: Vec<Self::Match>,
|
||||
_cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Get all of the matches for this query, should be done on the background
|
||||
fn find_matches(
|
||||
&mut self,
|
||||
query: project::search::SearchQuery,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Vec<Self::Match>> {
|
||||
if let TerminalContent::Connected(connected) = &self.content {
|
||||
let terminal = connected.read(cx).terminal().clone();
|
||||
terminal.update(cx, |term, cx| term.find_matches(query, cx))
|
||||
} else {
|
||||
Task::ready(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// Reports back to the search toolbar what the active match should be (the selection)
|
||||
fn active_match_index(
|
||||
&mut self,
|
||||
matches: Vec<Self::Match>,
|
||||
_cx: &mut ViewContext<Self>,
|
||||
) -> Option<usize> {
|
||||
if matches.len() > 0 {
|
||||
Some(0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///Get's the working directory for the given workspace, respecting the user's settings.
|
||||
|
|
|
@ -570,19 +570,25 @@ impl Element for TerminalElement {
|
|||
TerminalSize::new(line_height, cell_width, constraint.max)
|
||||
};
|
||||
|
||||
let search_matches = if let Some(terminal_model) = self.terminal.upgrade(cx) {
|
||||
terminal_model.read(cx).matches.clone()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let background_color = if self.modal {
|
||||
terminal_theme.colors.modal_background
|
||||
} else {
|
||||
terminal_theme.colors.background
|
||||
};
|
||||
|
||||
let (cells, selection, cursor, display_offset, cursor_text, search_matches, mode) = self
|
||||
let (cells, selection, cursor, display_offset, cursor_text, mode) = self
|
||||
.terminal
|
||||
.upgrade(cx)
|
||||
.unwrap()
|
||||
.update(cx.app, |terminal, cx| {
|
||||
terminal.set_size(dimensions);
|
||||
terminal.render_lock(cx, |content, cursor_text, search_matches| {
|
||||
terminal.render_lock(cx, |content, cursor_text| {
|
||||
let mut cells = vec![];
|
||||
cells.extend(
|
||||
content
|
||||
|
@ -605,7 +611,6 @@ impl Element for TerminalElement {
|
|||
content.cursor,
|
||||
content.display_offset,
|
||||
cursor_text,
|
||||
search_matches.clone(),
|
||||
content.mode,
|
||||
)
|
||||
})
|
||||
|
@ -613,12 +618,12 @@ impl Element for TerminalElement {
|
|||
|
||||
// searches, highlights to a single range representations
|
||||
let mut relative_highlighted_ranges = Vec::new();
|
||||
if let Some(selection) = selection {
|
||||
relative_highlighted_ranges.push((selection.start..=selection.end, selection_color));
|
||||
}
|
||||
for search_match in search_matches {
|
||||
relative_highlighted_ranges.push((search_match, match_color))
|
||||
}
|
||||
if let Some(selection) = selection {
|
||||
relative_highlighted_ranges.push((selection.start..=selection.end, selection_color));
|
||||
}
|
||||
|
||||
// then have that representation be converted to the appropriate highlight data structure
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::time::Duration;
|
||||
use std::{ops::RangeInclusive, time::Duration};
|
||||
|
||||
use alacritty_terminal::term::TermMode;
|
||||
use alacritty_terminal::{index::Point, term::TermMode};
|
||||
use context_menu::{ContextMenu, ContextMenuItem};
|
||||
use gpui::{
|
||||
actions,
|
||||
|
@ -8,8 +8,8 @@ use gpui::{
|
|||
geometry::vector::Vector2F,
|
||||
impl_internal_actions,
|
||||
keymap::Keystroke,
|
||||
AnyViewHandle, AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, View,
|
||||
ViewContext, ViewHandle,
|
||||
AnyViewHandle, AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, Task,
|
||||
View, ViewContext, ViewHandle,
|
||||
};
|
||||
use settings::{Settings, TerminalBlink};
|
||||
use smol::Timer;
|
||||
|
@ -58,8 +58,6 @@ pub fn init(cx: &mut MutableAppContext) {
|
|||
cx.add_action(TerminalView::paste);
|
||||
cx.add_action(TerminalView::clear);
|
||||
cx.add_action(TerminalView::show_character_palette);
|
||||
|
||||
cx.add_action(TerminalView::test_search);
|
||||
}
|
||||
|
||||
///A terminal view, maintains the PTY's file handles and communicates with the terminal
|
||||
|
@ -162,14 +160,6 @@ impl TerminalView {
|
|||
}
|
||||
}
|
||||
|
||||
fn test_search(&mut self, _: &SearchTest, cx: &mut ViewContext<Self>) {
|
||||
let search_string = "ttys";
|
||||
self.terminal.update(cx, |term, _| {
|
||||
term.search(search_string);
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
|
||||
self.terminal.update(cx, |term, _| term.clear());
|
||||
cx.notify();
|
||||
|
@ -246,6 +236,19 @@ impl TerminalView {
|
|||
.detach();
|
||||
}
|
||||
|
||||
pub fn find_matches(
|
||||
&mut self,
|
||||
query: project::search::SearchQuery,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Vec<RangeInclusive<Point>>> {
|
||||
self.terminal
|
||||
.update(cx, |term, cx| term.find_matches(query, cx))
|
||||
}
|
||||
|
||||
pub fn terminal(&self) -> &ModelHandle<Terminal> {
|
||||
&self.terminal
|
||||
}
|
||||
|
||||
fn next_blink_epoch(&mut self) -> usize {
|
||||
self.blink_epoch += 1;
|
||||
self.blink_epoch
|
||||
|
|
|
@ -10,8 +10,8 @@ use crate::{Item, ItemHandle, WeakItemHandle};
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum SearchEvent {
|
||||
ContentsUpdated,
|
||||
SelectionsChanged,
|
||||
MatchesInvalidated,
|
||||
ActiveMatchChanged,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -24,24 +24,27 @@ pub trait SearchableItem: Item {
|
|||
type Match: Any + Sync + Send + Clone;
|
||||
|
||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent>;
|
||||
fn clear_highlights(&mut self, cx: &mut ViewContext<Self>);
|
||||
fn highlight_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
|
||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
|
||||
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
|
||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
|
||||
fn select_next_match_in_direction(
|
||||
fn activate_next_match(
|
||||
&mut self,
|
||||
index: usize,
|
||||
direction: Direction,
|
||||
matches: Vec<Self::Match>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
);
|
||||
fn select_match_by_index(
|
||||
fn activate_match_at_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
matches: Vec<Self::Match>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
);
|
||||
fn matches(&mut self, query: SearchQuery, cx: &mut ViewContext<Self>)
|
||||
-> Task<Vec<Self::Match>>;
|
||||
fn find_matches(
|
||||
&mut self,
|
||||
query: SearchQuery,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Vec<Self::Match>>;
|
||||
fn active_match_index(
|
||||
&mut self,
|
||||
matches: Vec<Self::Match>,
|
||||
|
@ -107,11 +110,11 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
|||
}
|
||||
|
||||
fn clear_highlights(&self, cx: &mut MutableAppContext) {
|
||||
self.update(cx, |this, cx| this.clear_highlights(cx));
|
||||
self.update(cx, |this, cx| this.clear_matches(cx));
|
||||
}
|
||||
fn highlight_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut MutableAppContext) {
|
||||
let matches = downcast_matches(matches);
|
||||
self.update(cx, |this, cx| this.highlight_matches(matches, cx));
|
||||
self.update(cx, |this, cx| this.update_matches(matches, cx));
|
||||
}
|
||||
fn query_suggestion(&self, cx: &mut MutableAppContext) -> String {
|
||||
self.update(cx, |this, cx| this.query_suggestion(cx))
|
||||
|
@ -125,7 +128,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
|||
) {
|
||||
let matches = downcast_matches(matches);
|
||||
self.update(cx, |this, cx| {
|
||||
this.select_next_match_in_direction(index, direction, matches, cx)
|
||||
this.activate_next_match(index, direction, matches, cx)
|
||||
});
|
||||
}
|
||||
fn select_match_by_index(
|
||||
|
@ -136,7 +139,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
|||
) {
|
||||
let matches = downcast_matches(matches);
|
||||
self.update(cx, |this, cx| {
|
||||
this.select_match_by_index(index, matches, cx)
|
||||
this.activate_match_at_index(index, matches, cx)
|
||||
});
|
||||
}
|
||||
fn matches(
|
||||
|
@ -144,7 +147,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
|||
query: SearchQuery,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Task<Vec<Box<dyn Any + Send>>> {
|
||||
let matches = self.update(cx, |this, cx| this.matches(query, cx));
|
||||
let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
|
||||
cx.foreground().spawn(async {
|
||||
let matches = matches.await;
|
||||
matches
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue