1pub use buffer_search::BufferSearchBar;
2use editor::{display_map::ToDisplayPoint, Anchor, Bias, Editor, MultiBufferSnapshot};
3use gpui::{actions, impl_internal_actions, 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
18#[derive(Clone, PartialEq)]
19pub struct ToggleSearchOption(pub SearchOption);
20
21actions!(search, [SelectNextMatch, SelectPrevMatch]);
22impl_internal_actions!(search, [ToggleSearchOption]);
23
24#[derive(Clone, Copy, PartialEq)]
25pub enum SearchOption {
26 WholeWord,
27 CaseSensitive,
28 Regex,
29}
30
31#[derive(Clone, Copy, PartialEq, Eq)]
32pub enum Direction {
33 Prev,
34 Next,
35}
36
37pub(crate) fn active_match_index(
38 ranges: &[Range<Anchor>],
39 cursor: &Anchor,
40 buffer: &MultiBufferSnapshot,
41) -> Option<usize> {
42 if ranges.is_empty() {
43 None
44 } else {
45 match ranges.binary_search_by(|probe| {
46 if probe.end.cmp(&cursor, &*buffer).is_lt() {
47 Ordering::Less
48 } else if probe.start.cmp(&cursor, &*buffer).is_gt() {
49 Ordering::Greater
50 } else {
51 Ordering::Equal
52 }
53 }) {
54 Ok(i) | Err(i) => Some(cmp::min(i, ranges.len() - 1)),
55 }
56 }
57}
58
59pub(crate) fn match_index_for_direction(
60 ranges: &[Range<Anchor>],
61 cursor: &Anchor,
62 mut index: usize,
63 direction: Direction,
64 buffer: &MultiBufferSnapshot,
65) -> usize {
66 if ranges[index].start.cmp(&cursor, &buffer).is_gt() {
67 if direction == Direction::Prev {
68 if index == 0 {
69 index = ranges.len() - 1;
70 } else {
71 index -= 1;
72 }
73 }
74 } else if ranges[index].end.cmp(&cursor, &buffer).is_lt() {
75 if direction == Direction::Next {
76 index = 0;
77 }
78 } else if direction == Direction::Prev {
79 if index == 0 {
80 index = ranges.len() - 1;
81 } else {
82 index -= 1;
83 }
84 } else if direction == Direction::Next {
85 if index == ranges.len() - 1 {
86 index = 0
87 } else {
88 index += 1;
89 }
90 };
91 index
92}
93
94pub(crate) fn query_suggestion_for_editor(
95 editor: &ViewHandle<Editor>,
96 cx: &mut MutableAppContext,
97) -> String {
98 let display_map = editor
99 .update(cx, |editor, cx| editor.snapshot(cx))
100 .display_snapshot;
101 let selection = editor.read(cx).selections.newest::<usize>(cx);
102 if selection.start == selection.end {
103 let point = selection.start.to_display_point(&display_map);
104 let range = editor::movement::surrounding_word(&display_map, point);
105 let range = range.start.to_offset(&display_map, Bias::Left)
106 ..range.end.to_offset(&display_map, Bias::Right);
107 let text: String = display_map.buffer_snapshot.text_for_range(range).collect();
108 if text.trim().is_empty() {
109 String::new()
110 } else {
111 text
112 }
113 } else {
114 display_map
115 .buffer_snapshot
116 .text_for_range(selection.start..selection.end)
117 .collect()
118 }
119}