@@ -465,14 +465,8 @@ impl SearchData {
let match_offset_range = match_range.to_offset(multi_buffer_snapshot);
let mut search_match_indices = vec![
- multi_buffer_snapshot.clip_offset(
- match_offset_range.start - context_offset_range.start,
- Bias::Left,
- )
- ..multi_buffer_snapshot.clip_offset(
- match_offset_range.end - context_offset_range.start,
- Bias::Right,
- ),
+ match_offset_range.start - context_offset_range.start
+ ..match_offset_range.end - context_offset_range.start,
];
let entire_context_text = multi_buffer_snapshot
@@ -509,14 +503,8 @@ impl SearchData {
.next()
.is_some_and(|c| !c.is_whitespace());
search_match_indices.iter_mut().for_each(|range| {
- range.start = multi_buffer_snapshot.clip_offset(
- range.start.saturating_sub(left_whitespaces_offset),
- Bias::Left,
- );
- range.end = multi_buffer_snapshot.clip_offset(
- range.end.saturating_sub(left_whitespaces_offset),
- Bias::Right,
- );
+ range.start = range.start.saturating_sub(left_whitespaces_offset);
+ range.end = range.end.saturating_sub(left_whitespaces_offset);
});
let trimmed_row_offset_range =
@@ -5256,10 +5244,13 @@ mod tests {
use language::{Language, LanguageConfig, LanguageMatcher, tree_sitter_rust};
use pretty_assertions::assert_eq;
use project::FakeFs;
- use search::project_search::{self, perform_project_search};
+ use search::{
+ buffer_search,
+ project_search::{self, perform_project_search},
+ };
use serde_json::json;
use util::path;
- use workspace::{OpenOptions, OpenVisible};
+ use workspace::{OpenOptions, OpenVisible, ToolbarItemView};
use super::*;
@@ -5322,25 +5313,28 @@ mod tests {
ide/src/
inlay_hints/
fn_lifetime_fn.rs
- search: match config.param_names_for_lifetime_elision_hints {
- search: allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
- search: Some(it) if config.param_names_for_lifetime_elision_hints => {
- search: InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },
+ search: match config.«param_names_for_lifetime_elision_hints» {
+ search: allocated_lifetimes.push(if config.«param_names_for_lifetime_elision_hints» {
+ search: Some(it) if config.«param_names_for_lifetime_elision_hints» => {
+ search: InlayHintsConfig { «param_names_for_lifetime_elision_hints»: true, ..TEST_CONFIG },
inlay_hints.rs
- search: pub param_names_for_lifetime_elision_hints: bool,
- search: param_names_for_lifetime_elision_hints: self
+ search: pub «param_names_for_lifetime_elision_hints»: bool,
+ search: «param_names_for_lifetime_elision_hints»: self
static_index.rs
- search: param_names_for_lifetime_elision_hints: false,
+ search: «param_names_for_lifetime_elision_hints»: false,
rust-analyzer/src/
cli/
analysis_stats.rs
- search: param_names_for_lifetime_elision_hints: true,
+ search: «param_names_for_lifetime_elision_hints»: true,
config.rs
- search: param_names_for_lifetime_elision_hints: self"#
+ search: «param_names_for_lifetime_elision_hints»: self"#
.to_string();
let select_first_in_all_matches = |line_to_select: &str| {
- assert!(all_matches.contains(line_to_select));
+ assert!(
+ all_matches.contains(line_to_select),
+ "`{line_to_select}` was not found in all matches `{all_matches}`"
+ );
all_matches.replacen(
line_to_select,
&format!("{line_to_select}{SELECTED_MARKER}"),
@@ -5361,7 +5355,7 @@ mod tests {
cx,
),
select_first_in_all_matches(
- "search: match config.param_names_for_lifetime_elision_hints {"
+ "search: match config.«param_names_for_lifetime_elision_hints» {"
)
);
});
@@ -5401,16 +5395,16 @@ mod tests {
inlay_hints/
fn_lifetime_fn.rs{SELECTED_MARKER}
inlay_hints.rs
- search: pub param_names_for_lifetime_elision_hints: bool,
- search: param_names_for_lifetime_elision_hints: self
+ search: pub «param_names_for_lifetime_elision_hints»: bool,
+ search: «param_names_for_lifetime_elision_hints»: self
static_index.rs
- search: param_names_for_lifetime_elision_hints: false,
+ search: «param_names_for_lifetime_elision_hints»: false,
rust-analyzer/src/
cli/
analysis_stats.rs
- search: param_names_for_lifetime_elision_hints: true,
+ search: «param_names_for_lifetime_elision_hints»: true,
config.rs
- search: param_names_for_lifetime_elision_hints: self"#,
+ search: «param_names_for_lifetime_elision_hints»: self"#,
)
);
});
@@ -5471,9 +5465,9 @@ mod tests {
rust-analyzer/src/
cli/
analysis_stats.rs
- search: param_names_for_lifetime_elision_hints: true,
+ search: «param_names_for_lifetime_elision_hints»: true,
config.rs
- search: param_names_for_lifetime_elision_hints: self"#,
+ search: «param_names_for_lifetime_elision_hints»: self"#,
)
);
});
@@ -5553,21 +5547,21 @@ mod tests {
ide/src/
inlay_hints/
fn_lifetime_fn.rs
- search: match config.param_names_for_lifetime_elision_hints {
- search: allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
- search: Some(it) if config.param_names_for_lifetime_elision_hints => {
- search: InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },
+ search: match config.«param_names_for_lifetime_elision_hints» {
+ search: allocated_lifetimes.push(if config.«param_names_for_lifetime_elision_hints» {
+ search: Some(it) if config.«param_names_for_lifetime_elision_hints» => {
+ search: InlayHintsConfig { «param_names_for_lifetime_elision_hints»: true, ..TEST_CONFIG },
inlay_hints.rs
- search: pub param_names_for_lifetime_elision_hints: bool,
- search: param_names_for_lifetime_elision_hints: self
+ search: pub «param_names_for_lifetime_elision_hints»: bool,
+ search: «param_names_for_lifetime_elision_hints»: self
static_index.rs
- search: param_names_for_lifetime_elision_hints: false,
+ search: «param_names_for_lifetime_elision_hints»: false,
rust-analyzer/src/
cli/
analysis_stats.rs
- search: param_names_for_lifetime_elision_hints: true,
+ search: «param_names_for_lifetime_elision_hints»: true,
config.rs
- search: param_names_for_lifetime_elision_hints: self"#
+ search: «param_names_for_lifetime_elision_hints»: self"#
.to_string();
cx.executor()
@@ -5692,30 +5686,40 @@ mod tests {
ide/src/
inlay_hints/
fn_lifetime_fn.rs
- search: match config.param_names_for_lifetime_elision_hints {
- search: allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints {
- search: Some(it) if config.param_names_for_lifetime_elision_hints => {
- search: InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },
+ search: match config.«param_names_for_lifetime_elision_hints» {
+ search: allocated_lifetimes.push(if config.«param_names_for_lifetime_elision_hints» {
+ search: Some(it) if config.«param_names_for_lifetime_elision_hints» => {
+ search: InlayHintsConfig { «param_names_for_lifetime_elision_hints»: true, ..TEST_CONFIG },
inlay_hints.rs
- search: pub param_names_for_lifetime_elision_hints: bool,
- search: param_names_for_lifetime_elision_hints: self
+ search: pub «param_names_for_lifetime_elision_hints»: bool,
+ search: «param_names_for_lifetime_elision_hints»: self
static_index.rs
- search: param_names_for_lifetime_elision_hints: false,
+ search: «param_names_for_lifetime_elision_hints»: false,
rust-analyzer/src/
cli/
analysis_stats.rs
- search: param_names_for_lifetime_elision_hints: true,
+ search: «param_names_for_lifetime_elision_hints»: true,
config.rs
- search: param_names_for_lifetime_elision_hints: self"#
+ search: «param_names_for_lifetime_elision_hints»: self"#
.to_string();
let select_first_in_all_matches = |line_to_select: &str| {
- assert!(all_matches.contains(line_to_select));
+ assert!(
+ all_matches.contains(line_to_select),
+ "`{line_to_select}` was not found in all matches `{all_matches}`"
+ );
all_matches.replacen(
line_to_select,
&format!("{line_to_select}{SELECTED_MARKER}"),
1,
)
};
+ let clear_outline_metadata = |input: &str| {
+ input
+ .replace("search: ", "")
+ .replace("«", "")
+ .replace("»", "")
+ };
+
cx.executor()
.advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(100));
cx.run_until_parked();
@@ -5726,7 +5730,7 @@ mod tests {
.expect("should have an active editor open")
});
let initial_outline_selection =
- "search: match config.param_names_for_lifetime_elision_hints {";
+ "search: match config.«param_names_for_lifetime_elision_hints» {";
outline_panel.update_in(cx, |outline_panel, window, cx| {
assert_eq!(
display_entries(
@@ -5740,7 +5744,7 @@ mod tests {
);
assert_eq!(
selected_row_text(&active_editor, cx),
- initial_outline_selection.replace("search: ", ""), // Clear outline metadata prefixes
+ clear_outline_metadata(initial_outline_selection),
"Should place the initial editor selection on the corresponding search result"
);
@@ -5749,7 +5753,7 @@ mod tests {
});
let navigated_outline_selection =
- "search: Some(it) if config.param_names_for_lifetime_elision_hints => {";
+ "search: Some(it) if config.«param_names_for_lifetime_elision_hints» => {";
outline_panel.update(cx, |outline_panel, cx| {
assert_eq!(
display_entries(
@@ -5767,7 +5771,7 @@ mod tests {
outline_panel.update(cx, |_, cx| {
assert_eq!(
selected_row_text(&active_editor, cx),
- navigated_outline_selection.replace("search: ", ""), // Clear outline metadata prefixes
+ clear_outline_metadata(navigated_outline_selection),
"Should still have the initial caret position after SelectNext calls"
);
});
@@ -5778,7 +5782,7 @@ mod tests {
outline_panel.update(cx, |_outline_panel, cx| {
assert_eq!(
selected_row_text(&active_editor, cx),
- navigated_outline_selection.replace("search: ", ""), // Clear outline metadata prefixes
+ clear_outline_metadata(navigated_outline_selection),
"After opening, should move the caret to the opened outline entry's position"
);
});
@@ -5786,7 +5790,7 @@ mod tests {
outline_panel.update_in(cx, |outline_panel, window, cx| {
outline_panel.select_next(&SelectNext, window, cx);
});
- let next_navigated_outline_selection = "search: InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG },";
+ let next_navigated_outline_selection = "search: InlayHintsConfig { «param_names_for_lifetime_elision_hints»: true, ..TEST_CONFIG },";
outline_panel.update(cx, |outline_panel, cx| {
assert_eq!(
display_entries(
@@ -5804,7 +5808,7 @@ mod tests {
outline_panel.update(cx, |_outline_panel, cx| {
assert_eq!(
selected_row_text(&active_editor, cx),
- next_navigated_outline_selection.replace("search: ", ""), // Clear outline metadata prefixes
+ clear_outline_metadata(next_navigated_outline_selection),
"Should again preserve the selection after another SelectNext call"
);
});
@@ -5837,7 +5841,7 @@ mod tests {
);
assert_eq!(
selected_row_text(&new_active_editor, cx),
- next_navigated_outline_selection.replace("search: ", ""), // Clear outline metadata prefixes
+ clear_outline_metadata(next_navigated_outline_selection),
"When opening the excerpt, should navigate to the place corresponding the outline entry"
);
});
@@ -5939,11 +5943,11 @@ mod tests {
format!(
r#"one/
a.txt
- search: aaa aaa <==== selected
- search: aaa aaa
+ search: «aaa» aaa <==== selected
+ search: aaa «aaa»
two/
b.txt
- search: a aaa"#,
+ search: a «aaa»"#,
),
);
});
@@ -5969,7 +5973,7 @@ two/
a.txt <==== selected
two/
b.txt
- search: a aaa"#,
+ search: a «aaa»"#,
),
);
});
@@ -6018,7 +6022,7 @@ two/ <==== selected"#,
a.txt
two/ <==== selected
b.txt
- search: a aaa"#,
+ search: a «aaa»"#,
)
);
});
@@ -6483,18 +6487,18 @@ outline: struct OutlineEntryExcerpt
r#"frontend-project/
public/lottie/
syntax-tree.json
- search: {{ "something": "static" }} <==== selected
+ search: {{ "something": "«static»" }} <==== selected
src/
app/(site)/
(about)/jobs/[slug]/
page.tsx
- search: static
+ search: «static»
(blog)/post/[slug]/
page.tsx
- search: static
+ search: «static»
components/
ErrorBoundary.tsx
- search: static"#
+ search: «static»"#
)
);
});
@@ -6522,12 +6526,12 @@ outline: struct OutlineEntryExcerpt
r#"frontend-project/
public/lottie/
syntax-tree.json
- search: {{ "something": "static" }}
+ search: {{ "something": "«static»" }}
src/
app/(site)/ <==== selected
components/
ErrorBoundary.tsx
- search: static"#
+ search: «static»"#
)
);
});
@@ -6552,12 +6556,12 @@ outline: struct OutlineEntryExcerpt
r#"frontend-project/
public/lottie/
syntax-tree.json
- search: {{ "something": "static" }}
+ search: {{ "something": "«static»" }}
src/
app/(site)/
components/
ErrorBoundary.tsx
- search: static <==== selected"#
+ search: «static» <==== selected"#
)
);
});
@@ -6586,7 +6590,7 @@ outline: struct OutlineEntryExcerpt
r#"frontend-project/
public/lottie/
syntax-tree.json
- search: {{ "something": "static" }}
+ search: {{ "something": "«static»" }}
src/
app/(site)/
components/
@@ -6619,12 +6623,12 @@ outline: struct OutlineEntryExcerpt
r#"frontend-project/
public/lottie/
syntax-tree.json
- search: {{ "something": "static" }}
+ search: {{ "something": "«static»" }}
src/
app/(site)/
components/
ErrorBoundary.tsx <==== selected
- search: static"#
+ search: «static»"#
)
);
});
@@ -6667,18 +6671,18 @@ outline: struct OutlineEntryExcerpt
r#"frontend-project/
public/lottie/
syntax-tree.json
- search: {{ "something": "static" }}
+ search: {{ "something": "«static»" }}
src/
app/(site)/
(about)/jobs/[slug]/
page.tsx
- search: static
+ search: «static»
(blog)/post/[slug]/
page.tsx
- search: static
+ search: «static»
components/
ErrorBoundary.tsx <==== selected
- search: static"#
+ search: «static»"#
)
);
});
@@ -6784,16 +6788,21 @@ outline: struct OutlineEntryExcerpt
}
},
PanelEntry::Search(search_entry) => {
- format!(
- "search: {}",
- search_entry
- .render_data
- .get_or_init(|| SearchData::new(
- &search_entry.match_range,
- multi_buffer_snapshot
- ))
- .context_text
- )
+ let search_data = search_entry.render_data.get_or_init(|| {
+ SearchData::new(&search_entry.match_range, multi_buffer_snapshot)
+ });
+ let mut search_result = String::new();
+ let mut last_end = 0;
+ for range in &search_data.search_match_indices {
+ search_result.push_str(&search_data.context_text[last_end..range.start]);
+ search_result.push('«');
+ search_result.push_str(&search_data.context_text[range.start..range.end]);
+ search_result.push('»');
+ last_end = range.end;
+ }
+ search_result.push_str(&search_data.context_text[last_end..]);
+
+ format!("search: {search_result}")
}
};
@@ -6816,6 +6825,7 @@ outline: struct OutlineEntryExcerpt
workspace::init_settings(cx);
Project::init_settings(cx);
project_search::init(cx);
+ buffer_search::init(cx);
super::init(cx);
});
}
@@ -7827,4 +7837,102 @@ outline: fn main()"
};
});
}
+
+ #[gpui::test]
+ async fn test_buffer_search(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.background_executor.clone());
+ fs.insert_tree(
+ "/test",
+ json!({
+ "foo.txt": r#"<_constitution>
+
+</_constitution>
+
+
+
+## 📊 Output
+
+| Field | Meaning |
+"#
+ }),
+ )
+ .await;
+
+ let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
+ let workspace = add_outline_panel(&project, cx).await;
+ let cx = &mut VisualTestContext::from_window(*workspace, cx);
+
+ let editor = workspace
+ .update(cx, |workspace, window, cx| {
+ workspace.open_abs_path(
+ PathBuf::from("/test/foo.txt"),
+ OpenOptions {
+ visible: Some(OpenVisible::All),
+ ..OpenOptions::default()
+ },
+ window,
+ cx,
+ )
+ })
+ .unwrap()
+ .await
+ .unwrap()
+ .downcast::<Editor>()
+ .unwrap();
+
+ let search_bar = workspace
+ .update(cx, |_, window, cx| {
+ cx.new(|cx| {
+ let mut search_bar = BufferSearchBar::new(None, window, cx);
+ search_bar.set_active_pane_item(Some(&editor), window, cx);
+ search_bar.show(window, cx);
+ search_bar
+ })
+ })
+ .unwrap();
+
+ let outline_panel = outline_panel(&workspace, cx);
+
+ outline_panel.update_in(cx, |outline_panel, window, cx| {
+ outline_panel.set_active(true, window, cx)
+ });
+
+ search_bar
+ .update_in(cx, |search_bar, window, cx| {
+ search_bar.search(" ", None, true, window, cx)
+ })
+ .await
+ .unwrap();
+
+ cx.executor()
+ .advance_clock(UPDATE_DEBOUNCE + Duration::from_millis(500));
+ cx.run_until_parked();
+
+ outline_panel.update(cx, |outline_panel, cx| {
+ assert_eq!(
+ display_entries(
+ &project,
+ &snapshot(outline_panel, cx),
+ &outline_panel.cached_entries,
+ outline_panel.selected_entry(),
+ cx,
+ ),
+ "search: | Field« » | Meaning | <==== selected
+search: | Field « » | Meaning |
+search: | Field « » | Meaning |
+search: | Field « » | Meaning |
+search: | Field « »| Meaning |
+search: | Field | Meaning« » |
+search: | Field | Meaning « » |
+search: | Field | Meaning « » |
+search: | Field | Meaning « » |
+search: | Field | Meaning « » |
+search: | Field | Meaning « » |
+search: | Field | Meaning « » |
+search: | Field | Meaning « »|"
+ );
+ });
+ }
}