search stuff
This commit is contained in:
parent
f62b69adb4
commit
8e7d9cf22e
4 changed files with 140 additions and 55 deletions
|
@ -179,7 +179,7 @@ impl View for DiagnosticIndicator {
|
||||||
if in_progress {
|
if in_progress {
|
||||||
element.add_child(
|
element.add_child(
|
||||||
Label::new(
|
Label::new(
|
||||||
"checking…".into(),
|
"Checking…".into(),
|
||||||
style.diagnostic_message.default.text.clone(),
|
style.diagnostic_message.default.text.clone(),
|
||||||
)
|
)
|
||||||
.aligned()
|
.aligned()
|
||||||
|
|
78
crates/terminal/src/search.rs
Normal file
78
crates/terminal/src/search.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use std::{borrow::Cow, ops::Deref};
|
||||||
|
|
||||||
|
use alacritty_terminal::{
|
||||||
|
grid::Dimensions,
|
||||||
|
index::{Column, Direction, Line, Point},
|
||||||
|
term::search::{Match, RegexIter, RegexSearch},
|
||||||
|
Term,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_SEARCH_LINES: usize = 100;
|
||||||
|
|
||||||
|
///Header and impl fom alacritty/src/display/content.rs HintMatches
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SearchMatches<'a> {
|
||||||
|
/// All visible matches.
|
||||||
|
matches: Cow<'a, [Match]>,
|
||||||
|
|
||||||
|
/// Index of the last match checked.
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SearchMatches<'a> {
|
||||||
|
/// Create new renderable matches iterator..
|
||||||
|
fn new(matches: impl Into<Cow<'a, [Match]>>) -> Self {
|
||||||
|
Self {
|
||||||
|
matches: matches.into(),
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create from regex matches on term visable part.
|
||||||
|
pub fn visible_regex_matches<T>(term: &Term<T>, dfas: &RegexSearch) -> Self {
|
||||||
|
let matches = visible_regex_match_iter(term, dfas).collect::<Vec<_>>();
|
||||||
|
Self::new(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Advance the regex tracker to the next point.
|
||||||
|
///
|
||||||
|
/// This will return `true` if the point passed is part of a regex match.
|
||||||
|
fn advance(&mut self, point: Point) -> bool {
|
||||||
|
while let Some(bounds) = self.get(self.index) {
|
||||||
|
if bounds.start() > &point {
|
||||||
|
break;
|
||||||
|
} else if bounds.end() < &point {
|
||||||
|
self.index += 1;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for SearchMatches<'a> {
|
||||||
|
type Target = [Match];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.matches.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copied from alacritty/src/display/hint.rs
|
||||||
|
/// Iterate over all visible regex matches.
|
||||||
|
fn visible_regex_match_iter<'a, T>(
|
||||||
|
term: &'a Term<T>,
|
||||||
|
regex: &'a RegexSearch,
|
||||||
|
) -> impl Iterator<Item = Match> + 'a {
|
||||||
|
let viewport_start = Line(-(term.grid().display_offset() as i32));
|
||||||
|
let viewport_end = viewport_start + term.bottommost_line();
|
||||||
|
let mut start = term.line_search_left(Point::new(viewport_start, Column(0)));
|
||||||
|
let mut end = term.line_search_right(Point::new(viewport_end, Column(0)));
|
||||||
|
start.line = start.line.max(viewport_start - MAX_SEARCH_LINES);
|
||||||
|
end.line = end.line.min(viewport_end + MAX_SEARCH_LINES);
|
||||||
|
|
||||||
|
RegexIter::new(start, end, Direction::Right, term, regex)
|
||||||
|
.skip_while(move |rm| rm.end().line < viewport_start)
|
||||||
|
.take_while(move |rm| rm.start().line <= viewport_end)
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod mappings;
|
pub mod mappings;
|
||||||
pub mod modal;
|
pub mod modal;
|
||||||
|
pub mod search;
|
||||||
pub mod terminal_container_view;
|
pub mod terminal_container_view;
|
||||||
pub mod terminal_element;
|
pub mod terminal_element;
|
||||||
pub mod terminal_view;
|
pub mod terminal_view;
|
||||||
|
@ -13,7 +14,7 @@ use alacritty_terminal::{
|
||||||
index::{Direction, Point},
|
index::{Direction, Point},
|
||||||
selection::{Selection, SelectionType},
|
selection::{Selection, SelectionType},
|
||||||
sync::FairMutex,
|
sync::FairMutex,
|
||||||
term::{search::RegexSearch, RenderableContent, TermMode},
|
term::{color::Rgb, search::RegexSearch, RenderableContent, TermMode},
|
||||||
tty::{self, setup_env},
|
tty::{self, setup_env},
|
||||||
Term,
|
Term,
|
||||||
};
|
};
|
||||||
|
@ -81,18 +82,13 @@ pub enum Event {
|
||||||
BlinkChanged,
|
BlinkChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
enum Scroll {
|
|
||||||
AlacScroll(AlacScroll),
|
|
||||||
ToNextSearch,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
enum InternalEvent {
|
enum InternalEvent {
|
||||||
TermEvent(AlacTermEvent),
|
ColorRequest(usize, Arc<dyn Fn(Rgb) -> String + Sync + Send + 'static>),
|
||||||
Resize(TerminalSize),
|
Resize(TerminalSize),
|
||||||
Clear,
|
Clear,
|
||||||
Scroll(Scroll),
|
FocusNextMatch,
|
||||||
|
Scroll(AlacScroll),
|
||||||
SetSelection(Option<Selection>),
|
SetSelection(Option<Selection>),
|
||||||
UpdateSelection(Vector2F),
|
UpdateSelection(Vector2F),
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -172,8 +168,12 @@ impl From<TerminalSize> for WindowSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dimensions for TerminalSize {
|
impl Dimensions for TerminalSize {
|
||||||
|
/// Note: this is supposed to be for the back buffer's length,
|
||||||
|
/// but we exclusively use it to resize the terminal, which does not
|
||||||
|
/// use this method. We still have to implement it for the trait though,
|
||||||
|
/// hence, this comment.
|
||||||
fn total_lines(&self) -> usize {
|
fn total_lines(&self) -> usize {
|
||||||
self.screen_lines() //TODO: Check that this is fine. This is supposed to be for the back buffer...
|
self.screen_lines()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_lines(&self) -> usize {
|
fn screen_lines(&self) -> usize {
|
||||||
|
@ -378,7 +378,6 @@ impl TerminalBuilder {
|
||||||
cur_size: initial_size,
|
cur_size: initial_size,
|
||||||
last_mouse: None,
|
last_mouse: None,
|
||||||
last_offset: 0,
|
last_offset: 0,
|
||||||
has_selection: false,
|
|
||||||
searcher: None,
|
searcher: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -451,7 +450,6 @@ 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)>,
|
||||||
has_selection: bool,
|
|
||||||
searcher: Option<(Option<RegexSearch>, Point)>,
|
searcher: Option<(Option<RegexSearch>, Point)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,9 +490,11 @@ impl Terminal {
|
||||||
cx.emit(Event::Wakeup);
|
cx.emit(Event::Wakeup);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
AlacTermEvent::ColorRequest(_, _) => self
|
AlacTermEvent::ColorRequest(idx, fun_ptr) => {
|
||||||
.events
|
self.events
|
||||||
.push_back(InternalEvent::TermEvent(event.clone())),
|
.push_back(InternalEvent::ColorRequest(*idx, fun_ptr.clone()));
|
||||||
|
cx.notify(); //Immediately schedule a render to respond to the color request
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,14 +506,12 @@ impl Terminal {
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
InternalEvent::TermEvent(term_event) => {
|
InternalEvent::ColorRequest(index, format) => {
|
||||||
if let AlacTermEvent::ColorRequest(index, format) = term_event {
|
let color = term.colors()[*index].unwrap_or_else(|| {
|
||||||
let color = term.colors()[*index].unwrap_or_else(|| {
|
let term_style = &cx.global::<Settings>().theme.terminal;
|
||||||
let term_style = &cx.global::<Settings>().theme.terminal;
|
to_alac_rgb(get_color_at_index(index, &term_style.colors))
|
||||||
to_alac_rgb(get_color_at_index(index, &term_style.colors))
|
});
|
||||||
});
|
self.write_to_pty(format(color))
|
||||||
self.write_to_pty(format(color))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
InternalEvent::Resize(new_size) => {
|
InternalEvent::Resize(new_size) => {
|
||||||
self.cur_size = *new_size;
|
self.cur_size = *new_size;
|
||||||
|
@ -526,17 +524,24 @@ impl Terminal {
|
||||||
self.write_to_pty("\x0c".to_string());
|
self.write_to_pty("\x0c".to_string());
|
||||||
term.clear_screen(ClearMode::Saved);
|
term.clear_screen(ClearMode::Saved);
|
||||||
}
|
}
|
||||||
InternalEvent::Scroll(Scroll::AlacScroll(scroll)) => {
|
InternalEvent::Scroll(scroll) => {
|
||||||
term.scroll_display(*scroll);
|
term.scroll_display(*scroll);
|
||||||
}
|
}
|
||||||
InternalEvent::Scroll(Scroll::ToNextSearch) => {
|
InternalEvent::FocusNextMatch => {
|
||||||
if let Some((Some(searcher), origin)) = &self.searcher {
|
if let Some((Some(searcher), origin)) = &self.searcher {
|
||||||
match term.search_next(searcher, *origin, SEARCH_FORWARD, Direction::Left, None)
|
match term.search_next(searcher, *origin, SEARCH_FORWARD, Direction::Left, None)
|
||||||
{
|
{
|
||||||
Some(regex_match) => {
|
Some(regex_match) => {
|
||||||
//Jump to spot
|
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;
|
||||||
}
|
}
|
||||||
None => { /*reset state*/ }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -560,7 +565,6 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_select(&mut self, sel: Selection) {
|
fn begin_select(&mut self, sel: Selection) {
|
||||||
self.has_selection = true;
|
|
||||||
self.events
|
self.events
|
||||||
.push_back(InternalEvent::SetSelection(Some(sel)));
|
.push_back(InternalEvent::SetSelection(Some(sel)));
|
||||||
}
|
}
|
||||||
|
@ -571,35 +575,30 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_select(&mut self) {
|
fn end_select(&mut self) {
|
||||||
self.has_selection = false;
|
|
||||||
self.events.push_back(InternalEvent::SetSelection(None));
|
self.events.push_back(InternalEvent::SetSelection(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll(&mut self, scroll: AlacScroll) {
|
fn scroll(&mut self, scroll: AlacScroll) {
|
||||||
self.events
|
self.events.push_back(InternalEvent::Scroll(scroll));
|
||||||
.push_back(InternalEvent::Scroll(Scroll::AlacScroll(scroll)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_to_next_search(&mut self) {
|
fn focus_next_match(&mut self) {
|
||||||
self.events
|
self.events.push_back(InternalEvent::FocusNextMatch);
|
||||||
.push_back(InternalEvent::Scroll(Scroll::ToNextSearch));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
//Existing search, carry over origin
|
|
||||||
(Some(new_searcher), Some((_, origin))) => Some((Some(new_searcher), *origin)),
|
|
||||||
//No existing search, start a new one
|
|
||||||
(Some(new_searcher), None) => Some((Some(new_searcher), self.viewport_origin())),
|
|
||||||
//Error creating a new search, carry over origin
|
|
||||||
(None, Some((_, origin))) => Some((None, *origin)),
|
|
||||||
//Nothing to do :(
|
//Nothing to do :(
|
||||||
(None, None) => None,
|
(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 {
|
if let Some((Some(_), _)) = self.searcher {
|
||||||
self.scroll_to_next_search();
|
self.focus_next_match();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,7 +657,7 @@ 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) -> T,
|
F: FnOnce(RenderableContent, char, Option<RegexSearch>) -> T,
|
||||||
{
|
{
|
||||||
let m = self.term.clone(); //Arc clone
|
let m = self.term.clone(); //Arc clone
|
||||||
let mut term = m.lock();
|
let mut term = m.lock();
|
||||||
|
@ -672,14 +671,15 @@ impl Terminal {
|
||||||
|
|
||||||
let content = term.renderable_content();
|
let content = term.renderable_content();
|
||||||
|
|
||||||
// term.line_search_right(point)
|
|
||||||
// term.search_next(dfas, origin, direction, side, max_lines)
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
f(content, cursor_text)
|
f(
|
||||||
|
content,
|
||||||
|
cursor_text,
|
||||||
|
self.searcher.as_ref().and_then(|s| s.0.clone()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focus_in(&self) {
|
pub fn focus_in(&self) {
|
||||||
|
@ -854,6 +854,12 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_selection(from: Point, to: Point) -> Selection {
|
||||||
|
let mut focus = Selection::new(SelectionType::Simple, from, Direction::Left);
|
||||||
|
focus.update(to, Direction::Right);
|
||||||
|
focus
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for Terminal {
|
impl Drop for Terminal {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.pty_tx.0.send(Msg::Shutdown).ok();
|
self.pty_tx.0.send(Msg::Shutdown).ok();
|
||||||
|
|
|
@ -43,7 +43,7 @@ use crate::{
|
||||||
pub struct LayoutState {
|
pub struct LayoutState {
|
||||||
cells: Vec<LayoutCell>,
|
cells: Vec<LayoutCell>,
|
||||||
rects: Vec<LayoutRect>,
|
rects: Vec<LayoutRect>,
|
||||||
highlights: Vec<RelativeHighlightedRange>,
|
selections: Vec<RelativeHighlightedRange>,
|
||||||
cursor: Option<Cursor>,
|
cursor: Option<Cursor>,
|
||||||
background_color: Color,
|
background_color: Color,
|
||||||
selection_color: Color,
|
selection_color: Color,
|
||||||
|
@ -624,13 +624,13 @@ impl Element for TerminalElement {
|
||||||
terminal_theme.colors.background
|
terminal_theme.colors.background
|
||||||
};
|
};
|
||||||
|
|
||||||
let (cells, selection, cursor, display_offset, cursor_text, mode) = self
|
let (cells, selection, cursor, display_offset, cursor_text, searcher, mode) = self
|
||||||
.terminal
|
.terminal
|
||||||
.upgrade(cx)
|
.upgrade(cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.update(cx.app, |terminal, mcx| {
|
.update(cx.app, |terminal, mcx| {
|
||||||
terminal.set_size(dimensions);
|
terminal.set_size(dimensions);
|
||||||
terminal.render_lock(mcx, |content, cursor_text| {
|
terminal.render_lock(mcx, |content, cursor_text, searcher| {
|
||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
cells.extend(
|
cells.extend(
|
||||||
content
|
content
|
||||||
|
@ -653,12 +653,13 @@ impl Element for TerminalElement {
|
||||||
content.cursor,
|
content.cursor,
|
||||||
content.display_offset,
|
content.display_offset,
|
||||||
cursor_text,
|
cursor_text,
|
||||||
|
searcher,
|
||||||
content.mode,
|
content.mode,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (cells, rects, highlights) = TerminalElement::layout_grid(
|
let (cells, rects, selections) = TerminalElement::layout_grid(
|
||||||
cells,
|
cells,
|
||||||
&text_style,
|
&text_style,
|
||||||
&terminal_theme,
|
&terminal_theme,
|
||||||
|
@ -731,7 +732,7 @@ impl Element for TerminalElement {
|
||||||
selection_color,
|
selection_color,
|
||||||
size: dimensions,
|
size: dimensions,
|
||||||
rects,
|
rects,
|
||||||
highlights,
|
selections,
|
||||||
mode,
|
mode,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -769,13 +770,13 @@ impl Element for TerminalElement {
|
||||||
|
|
||||||
//Draw Selection
|
//Draw Selection
|
||||||
cx.paint_layer(clip_bounds, |cx| {
|
cx.paint_layer(clip_bounds, |cx| {
|
||||||
let start_y = layout.highlights.get(0).map(|highlight| {
|
let start_y = layout.selections.get(0).map(|highlight| {
|
||||||
origin.y() + highlight.line_index as f32 * layout.size.line_height
|
origin.y() + highlight.line_index as f32 * layout.size.line_height
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(y) = start_y {
|
if let Some(y) = start_y {
|
||||||
let range_lines = layout
|
let range_lines = layout
|
||||||
.highlights
|
.selections
|
||||||
.iter()
|
.iter()
|
||||||
.map(|relative_highlight| {
|
.map(|relative_highlight| {
|
||||||
relative_highlight.to_highlighted_range_line(origin, layout)
|
relative_highlight.to_highlighted_range_line(origin, layout)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue