Detailed changes
@@ -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());
-
- //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::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;
+ // }
+ // }
+ // }
+ // }
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