Merge branch 'main' into vim-search

This commit is contained in:
Conrad Irwin 2023-07-17 09:20:43 -06:00
commit f887a17ffe
111 changed files with 5714 additions and 1553 deletions

View file

@ -57,16 +57,16 @@ ordered-float.workspace = true
parking_lot.workspace = true
postage.workspace = true
pulldown-cmark = { version = "0.9.2", default-features = false }
rand = { workspace = true, optional = true }
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
smallvec.workspace = true
smol.workspace = true
tree-sitter-rust = { version = "*", optional = true }
tree-sitter-html = { version = "*", optional = true }
tree-sitter-javascript = { version = "*", optional = true }
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259", optional = true }
rand = { workspace = true, optional = true }
tree-sitter-rust = { workspace = true, optional = true }
tree-sitter-html = { workspace = true, optional = true }
tree-sitter-typescript = { workspace = true, optional = true }
[dev-dependencies]
copilot = { path = "../copilot", features = ["test-support"] }
@ -84,7 +84,6 @@ env_logger.workspace = true
rand.workspace = true
unindent.workspace = true
tree-sitter.workspace = true
tree-sitter-rust = "0.20"
tree-sitter-html = "0.19"
tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" }
tree-sitter-javascript = "0.20"
tree-sitter-rust.workspace = true
tree-sitter-html.workspace = true
tree-sitter-typescript.workspace = true

View file

@ -271,7 +271,9 @@ actions!(
SelectLargerSyntaxNode,
SelectSmallerSyntaxNode,
GoToDefinition,
GoToDefinitionSplit,
GoToTypeDefinition,
GoToTypeDefinitionSplit,
MoveToEnclosingBracket,
UndoSelection,
RedoSelection,
@ -407,7 +409,9 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(Editor::go_to_hunk);
cx.add_action(Editor::go_to_prev_hunk);
cx.add_action(Editor::go_to_definition);
cx.add_action(Editor::go_to_definition_split);
cx.add_action(Editor::go_to_type_definition);
cx.add_action(Editor::go_to_type_definition_split);
cx.add_action(Editor::fold);
cx.add_action(Editor::fold_at);
cx.add_action(Editor::unfold_lines);
@ -494,6 +498,7 @@ pub enum SoftWrap {
#[derive(Clone)]
pub struct EditorStyle {
pub text: TextStyle,
pub line_height_scalar: f32,
pub placeholder_text: Option<TextStyle>,
pub theme: theme::Editor,
pub theme_id: usize,
@ -6197,14 +6202,31 @@ impl Editor {
}
pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
}
pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
}
fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
}
pub fn go_to_type_definition_split(
&mut self,
_: &GoToTypeDefinitionSplit,
cx: &mut ViewContext<Self>,
) {
self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
}
fn go_to_definition_of_kind(
&mut self,
kind: GotoDefinitionKind,
split: bool,
cx: &mut ViewContext<Self>,
) {
let Some(workspace) = self.workspace(cx) else { return };
let buffer = self.buffer.read(cx);
let head = self.selections.newest::<usize>(cx).head();
@ -6223,7 +6245,7 @@ impl Editor {
cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
let definitions = definitions.await?;
editor.update(&mut cx, |editor, cx| {
editor.navigate_to_definitions(definitions, cx);
editor.navigate_to_definitions(definitions, split, cx);
})?;
Ok::<(), anyhow::Error>(())
})
@ -6233,6 +6255,7 @@ impl Editor {
pub fn navigate_to_definitions(
&mut self,
mut definitions: Vec<LocationLink>,
split: bool,
cx: &mut ViewContext<Editor>,
) {
let Some(workspace) = self.workspace(cx) else { return };
@ -6253,7 +6276,11 @@ impl Editor {
} else {
cx.window_context().defer(move |cx| {
let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
workspace.open_project_item(definition.target.buffer.clone(), cx)
if split {
workspace.split_project_item(definition.target.buffer.clone(), cx)
} else {
workspace.open_project_item(definition.target.buffer.clone(), cx)
}
});
target_editor.update(cx, |target_editor, cx| {
// When selecting a definition in a different buffer, disable the nav history
@ -6290,7 +6317,9 @@ impl Editor {
.map(|definition| definition.target)
.collect();
workspace.update(cx, |workspace, cx| {
Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
Self::open_locations_in_multibuffer(
workspace, locations, replica_id, title, split, cx,
)
});
});
}
@ -6335,7 +6364,7 @@ impl Editor {
})
.unwrap();
Self::open_locations_in_multibuffer(
workspace, locations, replica_id, title, cx,
workspace, locations, replica_id, title, false, cx,
);
})?;
@ -6350,6 +6379,7 @@ impl Editor {
mut locations: Vec<Location>,
replica_id: ReplicaId,
title: String,
split: bool,
cx: &mut ViewContext<Workspace>,
) {
// If there are multiple definitions, open them in a multibuffer
@ -6396,7 +6426,11 @@ impl Editor {
cx,
);
});
workspace.add_item(Box::new(editor), cx);
if split {
workspace.split_item(Box::new(editor), cx);
} else {
workspace.add_item(Box::new(editor), cx);
}
}
pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
@ -7237,6 +7271,47 @@ impl Editor {
}
results
}
pub fn background_highlights_in_range_for<T: 'static>(
&self,
search_range: Range<Anchor>,
display_snapshot: &DisplaySnapshot,
theme: &Theme,
) -> Vec<(Range<DisplayPoint>, Color)> {
let mut results = Vec::new();
let buffer = &display_snapshot.buffer_snapshot;
let Some((color_fetcher, ranges)) = self.background_highlights
.get(&TypeId::of::<T>()) else {
return vec![];
};
let color = color_fetcher(theme);
let start_ix = match ranges.binary_search_by(|probe| {
let cmp = probe.end.cmp(&search_range.start, buffer);
if cmp.is_gt() {
Ordering::Greater
} else {
Ordering::Less
}
}) {
Ok(i) | Err(i) => i,
};
for range in &ranges[start_ix..] {
if range.start.cmp(&search_range.end, buffer).is_ge() {
break;
}
let start = range
.start
.to_point(buffer)
.to_display_point(display_snapshot);
let end = range
.end
.to_point(buffer)
.to_display_point(display_snapshot);
results.push((start..end, color))
}
results
}
pub fn highlight_text<T: 'static>(
&mut self,
@ -7539,7 +7614,7 @@ impl Editor {
fn report_editor_event(
&self,
name: &'static str,
operation: &'static str,
file_extension: Option<String>,
cx: &AppContext,
) {
@ -7576,7 +7651,7 @@ impl Editor {
let event = ClickhouseEvent::Editor {
file_extension,
vim_mode,
operation: name,
operation,
copilot_enabled,
copilot_enabled_for_language,
};
@ -8075,7 +8150,7 @@ fn build_style(
cx: &AppContext,
) -> EditorStyle {
let font_cache = cx.font_cache();
let line_height_scalar = settings.line_height();
let theme_id = settings.theme.meta.id;
let mut theme = settings.theme.editor.clone();
let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
@ -8089,6 +8164,7 @@ fn build_style(
EditorStyle {
text: field_editor_theme.text,
placeholder_text: field_editor_theme.placeholder_text,
line_height_scalar,
theme,
theme_id,
}
@ -8111,6 +8187,7 @@ fn build_style(
underline: Default::default(),
},
placeholder_text: None,
line_height_scalar,
theme,
theme_id,
}

View file

@ -22,7 +22,10 @@ use language::{
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point,
};
use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings};
use project::FakeFs;
use std::sync::atomic;
use std::sync::atomic::AtomicUsize;
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
use unindent::Unindent;
use util::{
@ -1796,7 +1799,7 @@ async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
"});
}
// Ensure that comment continuations can be disabled.
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.extend_comment_on_newline = Some(false);
});
let mut cx = EditorTestContext::new(cx).await;
@ -3833,7 +3836,7 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
autoclose_before: "})]>".into(),
..Default::default()
},
Some(tree_sitter_javascript::language()),
Some(tree_sitter_typescript::language_tsx()),
));
let registry = Arc::new(LanguageRegistry::test());
@ -4546,7 +4549,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
assert!(!cx.read(|cx| editor.is_dirty(cx)));
// Set rust language override and assert overridden tabsize is sent to language server
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.languages.insert(
"Rust".into(),
LanguageSettingsContent {
@ -4660,7 +4663,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
assert!(!cx.read(|cx| editor.is_dirty(cx)));
// Set rust language override and assert overridden tabsize is sent to language server
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.languages.insert(
"Rust".into(),
LanguageSettingsContent {
@ -5380,7 +5383,7 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
line_comment: Some("// ".into()),
..Default::default()
},
Some(tree_sitter_javascript::language()),
Some(tree_sitter_typescript::language_tsx()),
));
let registry = Arc::new(LanguageRegistry::test());
@ -7084,6 +7087,233 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
});
}
#[gpui::test]
async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let language_name: Arc<str> = "Rust".into();
let mut language = Language::new(
LanguageConfig {
name: Arc::clone(&language_name),
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
Some(tree_sitter_rust::language()),
);
let server_restarts = Arc::new(AtomicUsize::new(0));
let closure_restarts = Arc::clone(&server_restarts);
let language_server_name = "test language server";
let mut fake_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
name: language_server_name,
initialization_options: Some(json!({
"testOptionValue": true
})),
initializer: Some(Box::new(move |fake_server| {
let task_restarts = Arc::clone(&closure_restarts);
fake_server.handle_request::<lsp::request::Shutdown, _, _>(move |_, _| {
task_restarts.fetch_add(1, atomic::Ordering::Release);
futures::future::ready(Ok(()))
});
})),
..Default::default()
}))
.await;
let fs = FakeFs::new(cx.background());
fs.insert_tree(
"/a",
json!({
"main.rs": "fn main() { let a = 5; }",
"other.rs": "// Test file",
}),
)
.await;
let project = Project::test(fs, ["/a".as_ref()], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
let (_, _workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let _buffer = project
.update(cx, |project, cx| {
project.open_local_buffer("/a/main.rs", cx)
})
.await
.unwrap();
let _fake_server = fake_servers.next().await.unwrap();
update_test_language_settings(cx, |language_settings| {
language_settings.languages.insert(
Arc::clone(&language_name),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
..Default::default()
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
0,
"Should not restart LSP server on an unrelated change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
"Some other server name".into(),
LspSettings {
initialization_options: Some(json!({
"some other init value": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
0,
"Should not restart LSP server on an unrelated LSP settings change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: Some(json!({
"anotherInitValue": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
1,
"Should restart LSP server on a related LSP settings change"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: Some(json!({
"anotherInitValue": false
})),
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
1,
"Should not restart LSP server on a related LSP settings change that is the same"
);
update_test_project_settings(cx, |project_settings| {
project_settings.lsp.insert(
language_server_name.into(),
LspSettings {
initialization_options: None,
},
);
});
cx.foreground().run_until_parked();
assert_eq!(
server_restarts.load(atomic::Ordering::Acquire),
2,
"Should restart LSP server on another related LSP settings change"
);
}
#[gpui::test]
async fn test_completions_with_additional_edits(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
completion_provider: Some(lsp::CompletionOptions {
trigger_characters: Some(vec![".".to_string()]),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
cx.set_state(indoc! {"fn main() { let a = 2ˇ; }"});
cx.simulate_keystroke(".");
let completion_item = lsp::CompletionItem {
label: "some".into(),
kind: Some(lsp::CompletionItemKind::SNIPPET),
detail: Some("Wrap the expression in an `Option::Some`".to_string()),
documentation: Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: "```rust\nSome(2)\n```".to_string(),
})),
deprecated: Some(false),
sort_text: Some("fffffff2".to_string()),
filter_text: Some("some".to_string()),
insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 22,
},
end: lsp::Position {
line: 0,
character: 22,
},
},
new_text: "Some(2)".to_string(),
})),
additional_text_edits: Some(vec![lsp::TextEdit {
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 20,
},
end: lsp::Position {
line: 0,
character: 22,
},
},
new_text: "".to_string(),
}]),
..Default::default()
};
let closure_completion_item = completion_item.clone();
let mut request = cx.handle_request::<lsp::request::Completion, _, _>(move |_, _, _| {
let task_completion_item = closure_completion_item.clone();
async move {
Ok(Some(lsp::CompletionResponse::Array(vec![
task_completion_item,
])))
}
});
request.next().await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
let apply_additional_edits = cx.update_editor(|editor, cx| {
editor
.confirm_completion(&ConfirmCompletion::default(), cx)
.unwrap()
});
cx.assert_editor_state(indoc! {"fn main() { let a = 2.Some(2)ˇ; }"});
cx.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _, _| {
let task_completion_item = completion_item.clone();
async move { Ok(task_completion_item) }
})
.next()
.await
.unwrap();
apply_additional_edits.await.unwrap();
cx.assert_editor_state(indoc! {"fn main() { let a = Some(2)ˇ; }"});
}
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
let point = DisplayPoint::new(row as u32, column as u32);
point..point
@ -7203,7 +7433,7 @@ fn handle_copilot_completion_request(
});
}
pub(crate) fn update_test_settings(
pub(crate) fn update_test_language_settings(
cx: &mut TestAppContext,
f: impl Fn(&mut AllLanguageSettingsContent),
) {
@ -7214,6 +7444,17 @@ pub(crate) fn update_test_settings(
});
}
pub(crate) fn update_test_project_settings(
cx: &mut TestAppContext,
f: impl Fn(&mut ProjectSettings),
) {
cx.update(|cx| {
cx.update_global::<SettingsStore, _, _>(|store, cx| {
store.update_user_settings::<ProjectSettings>(cx, f);
});
});
}
pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
cx.foreground().forbid_parking();
@ -7227,5 +7468,5 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC
crate::init(cx);
});
update_test_settings(cx, f);
update_test_language_settings(cx, f);
}

View file

@ -156,6 +156,7 @@ impl EditorElement {
event.position,
event.cmd,
event.shift,
event.alt,
position_map.as_ref(),
text_bounds,
cx,
@ -308,6 +309,7 @@ impl EditorElement {
position: Vector2F,
cmd: bool,
shift: bool,
alt: bool,
position_map: &PositionMap,
text_bounds: RectF,
cx: &mut EventContext<Editor>,
@ -324,9 +326,9 @@ impl EditorElement {
if point == target_point {
if shift {
go_to_fetched_type_definition(editor, point, cx);
go_to_fetched_type_definition(editor, point, alt, cx);
} else {
go_to_fetched_definition(editor, point, cx);
go_to_fetched_definition(editor, point, alt, cx);
}
return true;
@ -1086,11 +1088,13 @@ impl EditorElement {
})
}
};
for (row, _) in &editor.background_highlights_in_range(
start_anchor..end_anchor,
&layout.position_map.snapshot,
&theme,
) {
for (row, _) in &editor
.background_highlights_in_range_for::<crate::items::BufferSearchHighlights>(
start_anchor..end_anchor,
&layout.position_map.snapshot,
&theme,
)
{
let start_display = row.start;
let end_display = row.end;
@ -1180,8 +1184,10 @@ impl EditorElement {
});
scene.push_mouse_region(
MouseRegion::new::<ScrollbarMouseHandlers>(cx.view_id(), cx.view_id(), track_bounds)
.on_move(move |_, editor: &mut Editor, cx| {
editor.scroll_manager.show_scrollbar(cx);
.on_move(move |event, editor: &mut Editor, cx| {
if event.pressed_button.is_none() {
editor.scroll_manager.show_scrollbar(cx);
}
})
.on_down(MouseButton::Left, {
let row_range = row_range.clone();
@ -1971,7 +1977,7 @@ impl Element<Editor> for EditorElement {
let snapshot = editor.snapshot(cx);
let style = self.style.clone();
let line_height = style.text.line_height(cx.font_cache());
let line_height = (style.text.font_size * style.line_height_scalar).round();
let gutter_padding;
let gutter_width;
@ -2149,6 +2155,9 @@ impl Element<Editor> for EditorElement {
ShowScrollbar::Auto => {
// Git
(is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs())
||
// Selections
(is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty())
// Scrollmanager
|| editor.scroll_manager.scrollbars_visible()
}
@ -2911,7 +2920,7 @@ mod tests {
use super::*;
use crate::{
display_map::{BlockDisposition, BlockProperties},
editor_tests::{init_test, update_test_settings},
editor_tests::{init_test, update_test_language_settings},
Editor, MultiBuffer,
};
use gpui::TestAppContext;
@ -3108,7 +3117,7 @@ mod tests {
let resize_step = 10.0;
let mut editor_width = 200.0;
while editor_width <= 1000.0 {
update_test_settings(cx, |s| {
update_test_language_settings(cx, |s| {
s.defaults.tab_size = NonZeroU32::new(tab_size);
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
s.defaults.preferred_line_length = Some(editor_width as u32);

View file

@ -847,7 +847,7 @@ mod tests {
use text::Point;
use workspace::Workspace;
use crate::editor_tests::update_test_settings;
use crate::editor_tests::update_test_language_settings;
use super::*;
@ -1476,7 +1476,7 @@ mod tests {
),
] {
edits_made += 1;
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@ -1520,7 +1520,7 @@ mod tests {
edits_made += 1;
let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: false,
show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@ -1577,7 +1577,7 @@ mod tests {
let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
edits_made += 1;
update_test_settings(cx, |settings| {
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
@ -2269,7 +2269,7 @@ unedited (2nd) buffer should have the same hint");
crate::init(cx);
});
update_test_settings(cx, f);
update_test_language_settings(cx, f);
}
async fn prepare_test_objects(

View file

@ -883,14 +883,24 @@ impl ProjectItem for Editor {
}
}
enum BufferSearchHighlights {}
pub(crate) enum BufferSearchHighlights {}
impl SearchableItem for Editor {
type Match = Range<Anchor>;
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
fn to_search_event(
&mut self,
event: &Self::Event,
_: &mut ViewContext<Self>,
) -> Option<SearchEvent> {
match event {
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
Event::SelectionsChanged { .. } => Some(SearchEvent::ActiveMatchChanged),
Event::SelectionsChanged { .. } => {
if self.selections.disjoint_anchors().len() == 1 {
Some(SearchEvent::ActiveMatchChanged)
} else {
None
}
}
_ => None,
}
}
@ -942,6 +952,11 @@ impl SearchableItem for Editor {
})
}
fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
self.unfold_ranges(matches.clone(), false, false, cx);
self.change_selections(None, cx, |s| s.select_ranges(matches));
}
fn match_index_for_direction(
&mut self,
matches: &Vec<Range<Anchor>>,
@ -951,8 +966,17 @@ impl SearchableItem for Editor {
cx: &mut ViewContext<Self>,
) -> usize {
let buffer = self.buffer().read(cx).snapshot(cx);
let cursor = self.selections.newest_anchor().head();
if count.is_none() && matches[current_index].start.cmp(&cursor, &buffer).is_gt() {
let current_index_position = if self.selections.disjoint_anchors().len() == 1 {
self.selections.newest_anchor().head()
} else {
matches[current_index].start
};
if count.is_none()
&& matches[current_index]
.start
.cmp(&current_index_position, &buffer)
.is_gt()
{
if direction == Direction::Prev {
if current_index == 0 {
current_index = matches.len() - 1;
@ -960,7 +984,12 @@ impl SearchableItem for Editor {
current_index -= 1;
}
}
} else if count.is_none() && matches[current_index].end.cmp(&cursor, &buffer).is_lt() {
} else if count.is_none()
&& matches[current_index]
.end
.cmp(&current_index_position, &buffer)
.is_lt()
{
if direction == Direction::Next {
current_index = 0;
}

View file

@ -246,23 +246,26 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
pub fn go_to_fetched_definition(
editor: &mut Editor,
point: DisplayPoint,
split: bool,
cx: &mut ViewContext<Editor>,
) {
go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, cx);
go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, split, cx);
}
pub fn go_to_fetched_type_definition(
editor: &mut Editor,
point: DisplayPoint,
split: bool,
cx: &mut ViewContext<Editor>,
) {
go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, cx);
go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, split, cx);
}
fn go_to_fetched_definition_of_kind(
kind: LinkDefinitionKind,
editor: &mut Editor,
point: DisplayPoint,
split: bool,
cx: &mut ViewContext<Editor>,
) {
let cached_definitions = editor.link_go_to_definition_state.definitions.clone();
@ -275,7 +278,7 @@ fn go_to_fetched_definition_of_kind(
cx.focus_self();
}
editor.navigate_to_definitions(cached_definitions, cx);
editor.navigate_to_definitions(cached_definitions, split, cx);
} else {
editor.select(
SelectPhase::Begin {
@ -403,7 +406,7 @@ mod tests {
});
cx.update_editor(|editor, cx| {
go_to_fetched_type_definition(editor, hover_point, cx);
go_to_fetched_type_definition(editor, hover_point, false, cx);
});
requests.next().await;
cx.foreground().run_until_parked();
@ -614,7 +617,7 @@ mod tests {
// Cmd click with existing definition doesn't re-request and dismisses highlight
cx.update_editor(|editor, cx| {
go_to_fetched_definition(editor, hover_point, cx);
go_to_fetched_definition(editor, hover_point, false, cx);
});
// Assert selection moved to to definition
cx.lsp
@ -655,7 +658,7 @@ mod tests {
])))
});
cx.update_editor(|editor, cx| {
go_to_fetched_definition(editor, hover_point, cx);
go_to_fetched_definition(editor, hover_point, false, cx);
});
requests.next().await;
cx.foreground().run_until_parked();

View file

@ -16,13 +16,13 @@ use crate::{
Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
};
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct PendingSelection {
pub selection: Selection<Anchor>,
pub mode: SelectMode,
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct SelectionsCollection {
display_map: ModelHandle<DisplayMap>,
buffer: ModelHandle<MultiBuffer>,