search.rs

  1pub use buffer_search::BufferSearchBar;
  2use editor::{display_map::ToDisplayPoint, Anchor, Bias, Editor, MultiBufferSnapshot};
  3use gpui::{actions, Action, MutableAppContext, ViewHandle};
  4pub use project_search::{ProjectSearchBar, ProjectSearchView};
  5use std::{
  6    cmp::{self, Ordering},
  7    ops::Range,
  8};
  9
 10pub mod buffer_search;
 11pub mod project_search;
 12
 13pub fn init(cx: &mut MutableAppContext) {
 14    buffer_search::init(cx);
 15    project_search::init(cx);
 16}
 17
 18actions!(
 19    search,
 20    [
 21        ToggleWholeWord,
 22        ToggleCaseSensitive,
 23        ToggleRegex,
 24        SelectNextMatch,
 25        SelectPrevMatch
 26    ]
 27);
 28
 29#[derive(Clone, Copy, PartialEq)]
 30pub enum SearchOption {
 31    WholeWord,
 32    CaseSensitive,
 33    Regex,
 34}
 35
 36impl SearchOption {
 37    pub fn label(&self) -> &'static str {
 38        match self {
 39            SearchOption::WholeWord => "Match Whole Word",
 40            SearchOption::CaseSensitive => "Match Case",
 41            SearchOption::Regex => "Use Regular Expression",
 42        }
 43    }
 44
 45    pub fn to_toggle_action(&self) -> Box<dyn Action> {
 46        match self {
 47            SearchOption::WholeWord => Box::new(ToggleWholeWord),
 48            SearchOption::CaseSensitive => Box::new(ToggleCaseSensitive),
 49            SearchOption::Regex => Box::new(ToggleRegex),
 50        }
 51    }
 52}
 53
 54#[derive(Clone, Copy, PartialEq, Eq)]
 55pub enum Direction {
 56    Prev,
 57    Next,
 58}
 59
 60pub(crate) fn active_match_index(
 61    ranges: &[Range<Anchor>],
 62    cursor: &Anchor,
 63    buffer: &MultiBufferSnapshot,
 64) -> Option<usize> {
 65    if ranges.is_empty() {
 66        None
 67    } else {
 68        match ranges.binary_search_by(|probe| {
 69            if probe.end.cmp(&cursor, &*buffer).is_lt() {
 70                Ordering::Less
 71            } else if probe.start.cmp(&cursor, &*buffer).is_gt() {
 72                Ordering::Greater
 73            } else {
 74                Ordering::Equal
 75            }
 76        }) {
 77            Ok(i) | Err(i) => Some(cmp::min(i, ranges.len() - 1)),
 78        }
 79    }
 80}
 81
 82pub(crate) fn match_index_for_direction(
 83    ranges: &[Range<Anchor>],
 84    cursor: &Anchor,
 85    mut index: usize,
 86    direction: Direction,
 87    buffer: &MultiBufferSnapshot,
 88) -> usize {
 89    if ranges[index].start.cmp(&cursor, &buffer).is_gt() {
 90        if direction == Direction::Prev {
 91            if index == 0 {
 92                index = ranges.len() - 1;
 93            } else {
 94                index -= 1;
 95            }
 96        }
 97    } else if ranges[index].end.cmp(&cursor, &buffer).is_lt() {
 98        if direction == Direction::Next {
 99            index = 0;
100        }
101    } else if direction == Direction::Prev {
102        if index == 0 {
103            index = ranges.len() - 1;
104        } else {
105            index -= 1;
106        }
107    } else if direction == Direction::Next {
108        if index == ranges.len() - 1 {
109            index = 0
110        } else {
111            index += 1;
112        }
113    };
114    index
115}
116
117pub(crate) fn query_suggestion_for_editor(
118    editor: &ViewHandle<Editor>,
119    cx: &mut MutableAppContext,
120) -> String {
121    let display_map = editor
122        .update(cx, |editor, cx| editor.snapshot(cx))
123        .display_snapshot;
124    let selection = editor.read(cx).selections.newest::<usize>(cx);
125    if selection.start == selection.end {
126        let point = selection.start.to_display_point(&display_map);
127        let range = editor::movement::surrounding_word(&display_map, point);
128        let range = range.start.to_offset(&display_map, Bias::Left)
129            ..range.end.to_offset(&display_map, Bias::Right);
130        let text: String = display_map.buffer_snapshot.text_for_range(range).collect();
131        if text.trim().is_empty() {
132            String::new()
133        } else {
134            text
135        }
136    } else {
137        display_map
138            .buffer_snapshot
139            .text_for_range(selection.start..selection.end)
140            .collect()
141    }
142}