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",
|
"ctrl-cmd-space": "terminal::ShowCharacterPalette",
|
||||||
"cmd-c": "terminal::Copy",
|
"cmd-c": "terminal::Copy",
|
||||||
"cmd-v": "terminal::Paste",
|
"cmd-v": "terminal::Paste",
|
||||||
"cmd-k": "terminal::Clear",
|
"cmd-k": "terminal::Clear"
|
||||||
"cmd-s": "terminal::SearchTest"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -513,17 +513,17 @@ impl SearchableItem for Editor {
|
||||||
|
|
||||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
|
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
|
||||||
match event {
|
match event {
|
||||||
Event::BufferEdited => Some(SearchEvent::ContentsUpdated),
|
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
|
||||||
Event::SelectionsChanged { .. } => Some(SearchEvent::SelectionsChanged),
|
Event::SelectionsChanged { .. } => Some(SearchEvent::ActiveMatchChanged),
|
||||||
_ => None,
|
_ => 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);
|
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>(
|
self.highlight_background::<BufferSearchHighlights>(
|
||||||
matches,
|
matches,
|
||||||
|theme| theme.search.match_background,
|
|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,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
|
@ -575,7 +575,7 @@ impl SearchableItem for Editor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_match_by_index(
|
fn activate_match_at_index(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
matches: Vec<Range<Anchor>>,
|
matches: Vec<Range<Anchor>>,
|
||||||
|
@ -586,7 +586,7 @@ impl SearchableItem for Editor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches(
|
fn find_matches(
|
||||||
&mut self,
|
&mut self,
|
||||||
query: project::search::SearchQuery,
|
query: project::search::SearchQuery,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
|
|
|
@ -174,7 +174,9 @@ impl ToolbarItemView for BufferSearchBar {
|
||||||
cx,
|
cx,
|
||||||
Box::new(move |search_event, cx| {
|
Box::new(move |search_event, cx| {
|
||||||
if let Some(this) = handle.upgrade(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 {
|
match event {
|
||||||
SearchEvent::ContentsUpdated => self.update_matches(false, cx),
|
SearchEvent::MatchesInvalidated => self.update_matches(false, cx),
|
||||||
SearchEvent::SelectionsChanged => self.update_match_index(cx),
|
SearchEvent::ActiveMatchChanged => self.update_match_index(cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ use settings::{AlternateScroll, Settings, Shell, TerminalBlink};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
ops::Sub,
|
ops::{RangeInclusive, Sub},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
|
@ -48,7 +48,7 @@ use gpui::{
|
||||||
keymap::Keystroke,
|
keymap::Keystroke,
|
||||||
scene::{ClickRegionEvent, DownRegionEvent, DragRegionEvent, UpRegionEvent},
|
scene::{ClickRegionEvent, DownRegionEvent, DragRegionEvent, UpRegionEvent},
|
||||||
ClipboardItem, Entity, ModelContext, MouseButton, MouseMovedEvent, MutableAppContext,
|
ClipboardItem, Entity, ModelContext, MouseButton, MouseMovedEvent, MutableAppContext,
|
||||||
ScrollWheelEvent,
|
ScrollWheelEvent, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::mappings::{
|
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
|
///Scroll multiplier that is set to 3 by default. This will be removed when I
|
||||||
///Implement scroll bars.
|
///Implement scroll bars.
|
||||||
const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
|
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 MAX_SEARCH_LINES: usize = 100;
|
||||||
const DEBUG_TERMINAL_WIDTH: f32 = 500.;
|
const DEBUG_TERMINAL_WIDTH: f32 = 500.;
|
||||||
const DEBUG_TERMINAL_HEIGHT: f32 = 30.;
|
const DEBUG_TERMINAL_HEIGHT: f32 = 30.;
|
||||||
|
@ -91,7 +89,7 @@ enum InternalEvent {
|
||||||
ColorRequest(usize, Arc<dyn Fn(Rgb) -> String + Sync + Send + 'static>),
|
ColorRequest(usize, Arc<dyn Fn(Rgb) -> String + Sync + Send + 'static>),
|
||||||
Resize(TerminalSize),
|
Resize(TerminalSize),
|
||||||
Clear,
|
Clear,
|
||||||
FocusNextMatch,
|
// FocusNextMatch,
|
||||||
Scroll(AlacScroll),
|
Scroll(AlacScroll),
|
||||||
SetSelection(Option<Selection>),
|
SetSelection(Option<Selection>),
|
||||||
UpdateSelection(Vector2F),
|
UpdateSelection(Vector2F),
|
||||||
|
@ -382,7 +380,8 @@ impl TerminalBuilder {
|
||||||
cur_size: initial_size,
|
cur_size: initial_size,
|
||||||
last_mouse: None,
|
last_mouse: None,
|
||||||
last_offset: 0,
|
last_offset: 0,
|
||||||
searcher: None,
|
matches: Vec::new(),
|
||||||
|
selection_text: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(TerminalBuilder {
|
Ok(TerminalBuilder {
|
||||||
|
@ -454,7 +453,8 @@ pub struct Terminal {
|
||||||
last_mode: TermMode,
|
last_mode: TermMode,
|
||||||
last_offset: usize,
|
last_offset: usize,
|
||||||
last_mouse: Option<(Point, Direction)>,
|
last_mouse: Option<(Point, Direction)>,
|
||||||
searcher: Option<(Option<RegexSearch>, Point)>,
|
pub matches: Vec<RangeInclusive<Point>>,
|
||||||
|
pub selection_text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terminal {
|
impl Terminal {
|
||||||
|
@ -531,32 +531,32 @@ impl Terminal {
|
||||||
InternalEvent::Scroll(scroll) => {
|
InternalEvent::Scroll(scroll) => {
|
||||||
term.scroll_display(*scroll);
|
term.scroll_display(*scroll);
|
||||||
}
|
}
|
||||||
InternalEvent::FocusNextMatch => {
|
// InternalEvent::FocusNextMatch => {
|
||||||
if let Some((Some(searcher), _origin)) = &self.searcher {
|
// if let Some((Some(searcher), _origin)) = &self.searcher {
|
||||||
match term.search_next(
|
// match term.search_next(
|
||||||
searcher,
|
// searcher,
|
||||||
Point {
|
// Point {
|
||||||
line: Line(0),
|
// line: Line(0),
|
||||||
column: Column(0),
|
// column: Column(0),
|
||||||
},
|
// },
|
||||||
SEARCH_FORWARD,
|
// SEARCH_FORWARD,
|
||||||
Direction::Left,
|
// Direction::Left,
|
||||||
None,
|
// None,
|
||||||
) {
|
// ) {
|
||||||
Some(regex_match) => {
|
// Some(regex_match) => {
|
||||||
term.scroll_to_point(*regex_match.start());
|
// term.scroll_to_point(*regex_match.start());
|
||||||
|
|
||||||
//Focus is done with selections in zed
|
// //Focus is done with selections in zed
|
||||||
let focus = make_selection(*regex_match.start(), *regex_match.end());
|
// let focus = make_selection(*regex_match.start(), *regex_match.end());
|
||||||
term.selection = Some(focus);
|
// term.selection = Some(focus);
|
||||||
}
|
// }
|
||||||
None => {
|
// None => {
|
||||||
//Clear focused match
|
// //Clear focused match
|
||||||
term.selection = None;
|
// term.selection = None;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
InternalEvent::SetSelection(sel) => term.selection = sel.clone(),
|
InternalEvent::SetSelection(sel) => term.selection = sel.clone(),
|
||||||
InternalEvent::UpdateSelection(position) => {
|
InternalEvent::UpdateSelection(position) => {
|
||||||
if let Some(mut selection) = term.selection.take() {
|
if let Some(mut selection) = term.selection.take() {
|
||||||
|
@ -594,34 +594,34 @@ impl Terminal {
|
||||||
self.events.push_back(InternalEvent::Scroll(scroll));
|
self.events.push_back(InternalEvent::Scroll(scroll));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_next_match(&mut self) {
|
// fn focus_next_match(&mut self) {
|
||||||
self.events.push_back(InternalEvent::FocusNextMatch);
|
// self.events.push_back(InternalEvent::FocusNextMatch);
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn search(&mut self, search: &str) {
|
// pub fn search(&mut self, search: &str) {
|
||||||
let new_searcher = RegexSearch::new(search).ok();
|
// let new_searcher = RegexSearch::new(search).ok();
|
||||||
self.searcher = match (new_searcher, &self.searcher) {
|
// self.searcher = match (new_searcher, &self.searcher) {
|
||||||
//Nothing to do :(
|
// //Nothing to do :(
|
||||||
(None, None) => None,
|
// (None, None) => None,
|
||||||
//No existing search, start a new one
|
// //No existing search, start a new one
|
||||||
(Some(new_searcher), None) => Some((Some(new_searcher), self.viewport_origin())),
|
// (Some(new_searcher), None) => Some((Some(new_searcher), self.viewport_origin())),
|
||||||
//Existing search, carry over origin
|
// //Existing search, carry over origin
|
||||||
(new_searcher, Some((_, origin))) => Some((new_searcher, *origin)),
|
// (new_searcher, Some((_, origin))) => Some((new_searcher, *origin)),
|
||||||
};
|
// };
|
||||||
|
|
||||||
if let Some((Some(_), _)) = self.searcher {
|
// if let Some((Some(_), _)) = self.searcher {
|
||||||
self.focus_next_match();
|
// self.focus_next_match();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn viewport_origin(&mut self) -> Point {
|
// fn viewport_origin(&mut self) -> Point {
|
||||||
let viewport_top = alacritty_terminal::index::Line(-(self.last_offset as i32)) - 1;
|
// let viewport_top = alacritty_terminal::index::Line(-(self.last_offset as i32)) - 1;
|
||||||
Point::new(viewport_top, alacritty_terminal::index::Column(0))
|
// Point::new(viewport_top, alacritty_terminal::index::Column(0))
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn end_search(&mut self) {
|
// pub fn end_search(&mut self) {
|
||||||
self.searcher = None;
|
// self.searcher = None;
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn copy(&mut self) {
|
pub fn copy(&mut self) {
|
||||||
self.events.push_back(InternalEvent::Copy);
|
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
|
pub fn render_lock<F, T>(&mut self, cx: &mut ModelContext<Self>, f: F) -> T
|
||||||
where
|
where
|
||||||
F: FnOnce(RenderableContent, char, Vec<Match>) -> T,
|
F: FnOnce(RenderableContent, char) -> T,
|
||||||
{
|
{
|
||||||
let m = self.term.clone(); //Arc clone
|
let term = self.term.clone();
|
||||||
let mut term = m.lock();
|
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() {
|
while let Some(e) = self.events.pop_front() {
|
||||||
self.process_terminal_event(&e, &mut term, cx)
|
self.process_terminal_event(&e, &mut term, cx)
|
||||||
}
|
}
|
||||||
|
@ -683,16 +683,12 @@ impl Terminal {
|
||||||
|
|
||||||
let content = term.renderable_content();
|
let content = term.renderable_content();
|
||||||
|
|
||||||
|
self.selection_text = term.selection_to_string();
|
||||||
self.last_offset = content.display_offset;
|
self.last_offset = content.display_offset;
|
||||||
|
|
||||||
let cursor_text = term.grid()[content.cursor.point].c;
|
let cursor_text = term.grid()[content.cursor.point].c;
|
||||||
|
|
||||||
let mut matches = vec![];
|
f(content, cursor_text)
|
||||||
if let Some((Some(r), _)) = &self.searcher {
|
|
||||||
matches.extend(make_search_matches(&term, &r));
|
|
||||||
}
|
|
||||||
|
|
||||||
f(content, cursor_text, matches)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus_in(&self) {
|
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 {
|
impl Drop for Terminal {
|
||||||
|
@ -877,11 +900,11 @@ impl Entity for Terminal {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_selection(from: Point, to: Point) -> Selection {
|
// fn make_selection(from: Point, to: Point) -> Selection {
|
||||||
let mut focus = Selection::new(SelectionType::Simple, from, Direction::Left);
|
// let mut focus = Selection::new(SelectionType::Simple, from, Direction::Left);
|
||||||
focus.update(to, Direction::Right);
|
// focus.update(to, Direction::Right);
|
||||||
focus
|
// focus
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Copied from alacritty/src/display/hint.rs HintMatches::visible_regex_matches()
|
/// Copied from alacritty/src/display/hint.rs HintMatches::visible_regex_matches()
|
||||||
/// Iterate over all visible regex matches.
|
/// Iterate over all visible regex matches.
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
use crate::terminal_view::TerminalView;
|
use crate::terminal_view::TerminalView;
|
||||||
use crate::{Event, Terminal, TerminalBuilder, TerminalError};
|
use crate::{Event, Terminal, TerminalBuilder, TerminalError};
|
||||||
|
|
||||||
|
use alacritty_terminal::index::Point;
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, View,
|
actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, Task,
|
||||||
ViewContext, ViewHandle,
|
View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
|
use workspace::searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle};
|
||||||
use workspace::{Item, Workspace};
|
use workspace::{Item, Workspace};
|
||||||
|
|
||||||
use crate::TerminalSize;
|
use crate::TerminalSize;
|
||||||
use project::{LocalWorktree, Project, ProjectPath};
|
use project::{LocalWorktree, Project, ProjectPath};
|
||||||
use settings::{AlternateScroll, Settings, WorkingDirectory};
|
use settings::{AlternateScroll, Settings, WorkingDirectory};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::terminal_element::TerminalElement;
|
use crate::terminal_element::TerminalElement;
|
||||||
|
@ -328,6 +331,98 @@ impl Item for TerminalContainer {
|
||||||
fn should_close_item_on_event(event: &Self::Event) -> bool {
|
fn should_close_item_on_event(event: &Self::Event) -> bool {
|
||||||
matches!(event, &Event::CloseTerminal)
|
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.
|
///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)
|
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 {
|
let background_color = if self.modal {
|
||||||
terminal_theme.colors.modal_background
|
terminal_theme.colors.modal_background
|
||||||
} else {
|
} else {
|
||||||
terminal_theme.colors.background
|
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
|
.terminal
|
||||||
.upgrade(cx)
|
.upgrade(cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.update(cx.app, |terminal, cx| {
|
.update(cx.app, |terminal, cx| {
|
||||||
terminal.set_size(dimensions);
|
terminal.set_size(dimensions);
|
||||||
terminal.render_lock(cx, |content, cursor_text, search_matches| {
|
terminal.render_lock(cx, |content, cursor_text| {
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
cells.extend(
|
cells.extend(
|
||||||
content
|
content
|
||||||
|
@ -605,7 +611,6 @@ impl Element for TerminalElement {
|
||||||
content.cursor,
|
content.cursor,
|
||||||
content.display_offset,
|
content.display_offset,
|
||||||
cursor_text,
|
cursor_text,
|
||||||
search_matches.clone(),
|
|
||||||
content.mode,
|
content.mode,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -613,12 +618,12 @@ impl Element for TerminalElement {
|
||||||
|
|
||||||
// searches, highlights to a single range representations
|
// searches, highlights to a single range representations
|
||||||
let mut relative_highlighted_ranges = Vec::new();
|
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 {
|
for search_match in search_matches {
|
||||||
relative_highlighted_ranges.push((search_match, match_color))
|
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
|
// 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 context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
|
@ -8,8 +8,8 @@ use gpui::{
|
||||||
geometry::vector::Vector2F,
|
geometry::vector::Vector2F,
|
||||||
impl_internal_actions,
|
impl_internal_actions,
|
||||||
keymap::Keystroke,
|
keymap::Keystroke,
|
||||||
AnyViewHandle, AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, View,
|
AnyViewHandle, AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, Task,
|
||||||
ViewContext, ViewHandle,
|
View, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use settings::{Settings, TerminalBlink};
|
use settings::{Settings, TerminalBlink};
|
||||||
use smol::Timer;
|
use smol::Timer;
|
||||||
|
@ -58,8 +58,6 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_action(TerminalView::paste);
|
cx.add_action(TerminalView::paste);
|
||||||
cx.add_action(TerminalView::clear);
|
cx.add_action(TerminalView::clear);
|
||||||
cx.add_action(TerminalView::show_character_palette);
|
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
|
///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>) {
|
fn clear(&mut self, _: &Clear, cx: &mut ViewContext<Self>) {
|
||||||
self.terminal.update(cx, |term, _| term.clear());
|
self.terminal.update(cx, |term, _| term.clear());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -246,6 +236,19 @@ impl TerminalView {
|
||||||
.detach();
|
.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 {
|
fn next_blink_epoch(&mut self) -> usize {
|
||||||
self.blink_epoch += 1;
|
self.blink_epoch += 1;
|
||||||
self.blink_epoch
|
self.blink_epoch
|
||||||
|
|
|
@ -10,8 +10,8 @@ use crate::{Item, ItemHandle, WeakItemHandle};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SearchEvent {
|
pub enum SearchEvent {
|
||||||
ContentsUpdated,
|
MatchesInvalidated,
|
||||||
SelectionsChanged,
|
ActiveMatchChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -24,24 +24,27 @@ pub trait SearchableItem: Item {
|
||||||
type Match: Any + Sync + Send + Clone;
|
type Match: Any + Sync + Send + Clone;
|
||||||
|
|
||||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent>;
|
fn to_search_event(event: &Self::Event) -> Option<SearchEvent>;
|
||||||
fn clear_highlights(&mut self, cx: &mut ViewContext<Self>);
|
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
|
||||||
fn highlight_matches(&mut self, matches: Vec<Self::Match>, 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 query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
|
||||||
fn select_next_match_in_direction(
|
fn activate_next_match(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
matches: Vec<Self::Match>,
|
matches: Vec<Self::Match>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
);
|
);
|
||||||
fn select_match_by_index(
|
fn activate_match_at_index(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
matches: Vec<Self::Match>,
|
matches: Vec<Self::Match>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
);
|
);
|
||||||
fn matches(&mut self, query: SearchQuery, cx: &mut ViewContext<Self>)
|
fn find_matches(
|
||||||
-> Task<Vec<Self::Match>>;
|
&mut self,
|
||||||
|
query: SearchQuery,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Task<Vec<Self::Match>>;
|
||||||
fn active_match_index(
|
fn active_match_index(
|
||||||
&mut self,
|
&mut self,
|
||||||
matches: Vec<Self::Match>,
|
matches: Vec<Self::Match>,
|
||||||
|
@ -107,11 +110,11 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_highlights(&self, cx: &mut MutableAppContext) {
|
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) {
|
fn highlight_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut MutableAppContext) {
|
||||||
let matches = downcast_matches(matches);
|
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 {
|
fn query_suggestion(&self, cx: &mut MutableAppContext) -> String {
|
||||||
self.update(cx, |this, cx| this.query_suggestion(cx))
|
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);
|
let matches = downcast_matches(matches);
|
||||||
self.update(cx, |this, cx| {
|
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(
|
fn select_match_by_index(
|
||||||
|
@ -136,7 +139,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
||||||
) {
|
) {
|
||||||
let matches = downcast_matches(matches);
|
let matches = downcast_matches(matches);
|
||||||
self.update(cx, |this, cx| {
|
self.update(cx, |this, cx| {
|
||||||
this.select_match_by_index(index, matches, cx)
|
this.activate_match_at_index(index, matches, cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn matches(
|
fn matches(
|
||||||
|
@ -144,7 +147,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
||||||
query: SearchQuery,
|
query: SearchQuery,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Task<Vec<Box<dyn Any + Send>>> {
|
) -> 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 {
|
cx.foreground().spawn(async {
|
||||||
let matches = matches.await;
|
let matches = matches.await;
|
||||||
matches
|
matches
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue