diff --git a/Cargo.toml b/Cargo.toml index 054f6324629626..8c986abc22b67a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -800,13 +800,15 @@ calloop = { git = "https://github.com/zed-industries/calloop" } [profile.dev] split-debuginfo = "unpacked" +debug = 1 incremental = true -codegen-units = 16 +codegen-units = 256 # mirror configuration for crates compiled for the build platform # (without this cargo will compile ~400 crates twice) [profile.dev.build-override] -codegen-units = 16 +debug = 1 +codegen-units = 256 [profile.dev.package] # proc-macros start diff --git a/assets/settings/default.json b/assets/settings/default.json index bd41f1704f6be5..79024021fedbd2 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -310,6 +310,13 @@ // The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created. "delay": 300, }, + // Code lens settings for showing reference counts and other metadata above code elements. + "code_lens": { + // Whether code lens is enabled. + "enabled": true, + // The debounce delay in milliseconds before querying code lens from the language server. + "debounce": 300 + }, // What to do when go to definition yields no results. // // 1. Do nothing: `none` diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 9a32fe2230785a..df077e12ced2c8 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -825,6 +825,8 @@ actions!( ToggleIndentGuides, /// Toggles inlay hints display. ToggleInlayHints, + /// Toggles code lens display. + ToggleCodeLens, /// Toggles inline values display. ToggleInlineValues, /// Toggles inline diagnostics display. diff --git a/crates/editor/src/code_lens.rs b/crates/editor/src/code_lens.rs new file mode 100644 index 00000000000000..92d209c660431e --- /dev/null +++ b/crates/editor/src/code_lens.rs @@ -0,0 +1,435 @@ +use std::ops::Range; + +use collections::HashMap; +use gpui::{App, SharedString, Task, WeakEntity}; +use language::BufferId; +use multi_buffer::{Anchor, MultiBufferSnapshot, ToPoint as _}; +use project::CodeAction; +use settings::Settings; +use ui::{Context, Window, div, prelude::*}; + +use crate::{ + Editor, FindAllReferences, GoToImplementation, SelectionEffects, + display_map::{BlockPlacement, BlockProperties, BlockStyle, CustomBlockId}, +}; + +#[derive(Clone, Debug)] +pub struct CodeLensItem { + pub text: SharedString, + pub action: Option, +} + +#[derive(Clone, Debug)] +pub struct CodeLensData { + pub position: Anchor, + pub items: Vec, +} + +#[derive(Default)] +pub struct CodeLensCache { + enabled: bool, + lenses: HashMap>, + pending_refresh: HashMap>, + block_ids: HashMap>, +} + +impl CodeLensCache { + pub fn new(enabled: bool) -> Self { + Self { + enabled, + lenses: HashMap::default(), + pending_refresh: HashMap::default(), + block_ids: HashMap::default(), + } + } + + pub fn toggle(&mut self, enabled: bool) -> bool { + if self.enabled == enabled { + return false; + } + self.enabled = enabled; + if !enabled { + self.clear(); + } + true + } + + pub fn clear(&mut self) { + self.lenses.clear(); + self.pending_refresh.clear(); + self.block_ids.clear(); + } + + pub fn enabled(&self) -> bool { + self.enabled + } + + pub fn get_lenses_for_buffer(&self, buffer_id: BufferId) -> Option<&Vec> { + self.lenses.get(&buffer_id) + } + + pub fn set_lenses_for_buffer(&mut self, buffer_id: BufferId, lenses: Vec) { + self.lenses.insert(buffer_id, lenses); + } + + pub fn set_block_ids(&mut self, buffer_id: BufferId, block_ids: Vec) { + self.block_ids.insert(buffer_id, block_ids); + } + + pub fn get_block_ids(&self, buffer_id: &BufferId) -> Option<&Vec> { + self.block_ids.get(buffer_id) + } + + #[allow(dead_code)] + pub fn remove_buffer(&mut self, buffer_id: &BufferId) { + self.lenses.remove(buffer_id); + self.pending_refresh.remove(buffer_id); + self.block_ids.remove(buffer_id); + } + + pub fn set_refresh_task(&mut self, buffer_id: BufferId, task: Task<()>) { + self.pending_refresh.insert(buffer_id, task); + } + + pub fn remove_refresh_task(&mut self, buffer_id: &BufferId) { + self.pending_refresh.remove(buffer_id); + } +} + +fn group_lenses_by_row( + lenses: Vec<(Anchor, CodeLensItem)>, + snapshot: &MultiBufferSnapshot, +) -> Vec { + let mut grouped: HashMap)> = HashMap::default(); + + for (position, item) in lenses { + let row = position.to_point(snapshot).row; + grouped + .entry(row) + .or_insert_with(|| (position, Vec::new())) + .1 + .push(item); + } + + let mut result: Vec = grouped + .into_iter() + .map(|(_, (position, items))| CodeLensData { position, items }) + .collect(); + + result.sort_by_key(|lens| lens.position.to_point(snapshot).row); + result +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum CodeLensKind { + References, + Implementations, + Other, +} + +fn detect_lens_kind(title: &str) -> CodeLensKind { + let title_lower = title.to_lowercase(); + if title_lower.contains("reference") { + CodeLensKind::References + } else if title_lower.contains("implementation") { + CodeLensKind::Implementations + } else { + CodeLensKind::Other + } +} + +fn should_hide_lens(title: &str) -> bool { + title.starts_with("0 ") // 0 reference or 0 implementation +} + +fn render_code_lens_line( + lens: CodeLensData, + editor: WeakEntity, +) -> impl Fn(&mut crate::display_map::BlockContext) -> gpui::AnyElement { + move |cx| { + let mut children: Vec = Vec::new(); + + for (i, item) in lens.items.iter().enumerate() { + if i > 0 { + children.push( + div() + .text_ui_xs(cx.app) + .text_color(cx.app.theme().colors().text_muted) + .child(" | ") + .into_any_element(), + ); + } + + let text = item.text.clone(); + let action = item.action.clone(); + let editor_clone = editor.clone(); + let position = lens.position; + + children.push( + div() + .id(SharedString::from(format!("code-lens-{}-{}", i, text))) + .text_ui_xs(cx.app) + .text_color(cx.app.theme().colors().text_muted) + .cursor_pointer() + .hover(|style| style.text_color(cx.app.theme().colors().text)) + .child(text.clone()) + .on_click({ + let text = text.clone(); + move |_event, window, cx| { + let kind = detect_lens_kind(&text); + if let Some(editor) = editor_clone.upgrade() { + _ = editor.update(cx, |editor, cx| { + editor.change_selections( + SelectionEffects::default(), + window, + cx, + |s| { + s.select_anchor_ranges([position..position]); + }, + ); + + match kind { + CodeLensKind::References => { + if let Some(task) = editor.find_all_references( + &FindAllReferences::default(), + window, + cx, + ) { + task.detach_and_log_err(cx); + } + } + CodeLensKind::Implementations => { + editor + .go_to_implementation( + &GoToImplementation, + window, + cx, + ) + .detach_and_log_err(cx); + } + CodeLensKind::Other => { + if let Some(action) = &action { + if let Some(workspace) = editor.workspace() { + let project = + workspace.read(cx).project().clone(); + let action = action.clone(); + let buffer = editor.buffer().clone(); + if let Some(excerpt_buffer) = + buffer.read(cx).as_singleton() + { + project + .update(cx, |project, cx| { + project.apply_code_action( + excerpt_buffer.clone(), + action, + true, + cx, + ) + }) + .detach_and_log_err(cx); + } + } + } + } + } + }); + } + } + }) + .into_any_element(), + ); + } + + div() + .pl(cx.anchor_x) + .flex() + .flex_row() + .items_center() + .children(children) + .into_any_element() + } +} + +impl Editor { + pub fn code_lens_enabled(&self, cx: &App) -> bool { + crate::EditorSettings::get_global(cx).code_lens.enabled + } + + pub fn refresh_code_lenses( + &mut self, + window: &mut Window, + cx: &mut Context, + ) -> Option> { + if !self.code_lens_enabled(cx) { + return None; + } + + let buffer = self.buffer().read(cx); + let excerpt_buffer = match buffer.as_singleton() { + Some(b) => b, + None => return None, + }; + let buffer_id = excerpt_buffer.read(cx).remote_id(); + let excerpt_buffer = excerpt_buffer.clone(); + + let Some(project) = self.project.clone() else { + return None; + }; + + let text_range = text::Anchor::MIN..text::Anchor::MAX; + let multibuffer = self.buffer().clone(); + + let task = cx.spawn_in(window, async move |editor, cx| { + let actions_task = project.update(cx, |project, cx| { + project.code_lens_actions::(&excerpt_buffer, text_range.clone(), cx) + }); + + let actions: anyhow::Result>> = actions_task.await; + + if let Ok(Some(actions)) = actions { + let lenses = multibuffer.update(cx, |multibuffer, cx| { + let snapshot = multibuffer.snapshot(cx); + + let individual_lenses: Vec<(Anchor, CodeLensItem)> = actions + .into_iter() + .filter_map(|action| { + let position = snapshot.anchor_in_excerpt( + snapshot.excerpts().next()?.0, + action.range.start, + )?; + + let text = match &action.lsp_action { + project::LspAction::CodeLens(lens) => { + lens.command.as_ref().map(|cmd| cmd.title.clone()) + } + _ => None, + }; + + text.and_then(|text| { + if should_hide_lens(&text) { + None + } else { + Some(( + position, + CodeLensItem { + text: text.into(), + action: Some(action), + }, + )) + } + }) + }) + .collect(); + + group_lenses_by_row(individual_lenses, &snapshot) + }); + + if let Err(_) = editor.update(cx, |editor, cx| { + if let Some(old_block_ids) = editor.code_lens_cache.get_block_ids(&buffer_id) { + editor.remove_blocks(old_block_ids.iter().copied().collect(), None, cx); + } + + editor + .code_lens_cache + .set_lenses_for_buffer(buffer_id, lenses.clone()); + + let editor_handle = cx.entity().downgrade(); + + let blocks = lenses + .into_iter() + .map(|lens| { + let position = lens.position; + let render_fn = render_code_lens_line(lens, editor_handle.clone()); + BlockProperties { + placement: BlockPlacement::Above(position), + height: Some(1), + style: BlockStyle::Sticky, + render: std::sync::Arc::new(render_fn), + priority: 0, + } + }) + .collect::>(); + + let block_ids = editor.insert_blocks(blocks, None, cx); + editor.code_lens_cache.set_block_ids(buffer_id, block_ids); + cx.notify(); + }) { + editor + .update(cx, |editor, _cx| { + editor.code_lens_cache.remove_refresh_task(&buffer_id); + }) + .ok(); + return; + } + } + + editor + .update(cx, |editor, _cx| { + editor.code_lens_cache.remove_refresh_task(&buffer_id); + }) + .ok(); + }); + + self.code_lens_cache.set_refresh_task(buffer_id, task); + None + } + + pub fn toggle_code_lenses( + &mut self, + _: &crate::actions::ToggleCodeLens, + window: &mut Window, + cx: &mut Context, + ) { + let enabled = !self.code_lens_cache.enabled(); + if self.code_lens_cache.toggle(enabled) { + if enabled { + self.refresh_code_lenses(window, cx); + } else { + let all_block_ids: Vec = self + .code_lens_cache + .block_ids + .values() + .flat_map(|ids| ids.iter().copied()) + .collect(); + if !all_block_ids.is_empty() { + self.remove_blocks(all_block_ids.into_iter().collect(), None, cx); + } + } + cx.notify(); + } + } + + pub fn get_code_lenses_for_visible_range( + &self, + range: Range, + cx: &App, + ) -> Vec { + if !self.code_lens_enabled(cx) { + return Vec::new(); + } + + let buffer = self.buffer().read(cx); + let Some(excerpt_buffer) = buffer.as_singleton() else { + return Vec::new(); + }; + + let buffer_id = excerpt_buffer.read(cx).remote_id(); + let snapshot = buffer.snapshot(cx); + + let Some(lenses) = self.code_lens_cache.get_lenses_for_buffer(buffer_id) else { + return Vec::new(); + }; + + let start_point = range.start.to_point(&snapshot); + let end_point = range.end.to_point(&snapshot); + + lenses + .iter() + .filter(|lens| { + let point = lens.position.to_point(&snapshot); + point.row >= start_point.row && point.row <= end_point.row + }) + .cloned() + .collect() + } +} diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 7282ad6cce76fe..f6227b1887a607 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -16,6 +16,7 @@ pub mod blink_manager; mod bracket_colorization; mod clangd_ext; pub mod code_context_menus; +mod code_lens; pub mod display_map; mod editor_settings; mod element; @@ -1329,6 +1330,7 @@ pub struct Editor { post_scroll_update: Task<()>, refresh_colors_task: Task<()>, inlay_hints: Option, + code_lens_cache: code_lens::CodeLensCache, folding_newlines: Task<()>, select_next_is_case_sensitive: Option, pub lookup_key: Option>, @@ -2112,7 +2114,7 @@ impl Editor { window, |editor, _, event, window, cx| match event { project::Event::RefreshCodeLens => { - // we always query lens with actions, without storing them, always refreshing them + editor.refresh_code_lenses(window, cx); } project::Event::RefreshInlayHints { server_id, @@ -2169,6 +2171,7 @@ impl Editor { refresh_linked_ranges(editor, window, cx); editor.refresh_code_actions(window, cx); editor.refresh_document_highlights(cx); + editor.refresh_code_lenses(window, cx); } } @@ -2517,6 +2520,9 @@ impl Editor { colors: None, refresh_colors_task: Task::ready(()), inlay_hints: None, + code_lens_cache: code_lens::CodeLensCache::new( + EditorSettings::get_global(cx).code_lens.enabled, + ), next_color_inlay_id: 0, post_scroll_update: Task::ready(()), linked_edit_ranges: Default::default(), @@ -2603,6 +2609,7 @@ impl Editor { cx, ); editor.colorize_brackets(false, cx); + editor.refresh_code_lenses(window, cx); }) .ok(); }); diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 0b19edf0393163..da079448f0fa2c 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -59,6 +59,7 @@ pub struct EditorSettings { pub minimum_contrast_for_highlights: f32, pub completion_menu_scrollbar: ShowScrollbar, pub completion_detail_alignment: CompletionDetailAlignment, + pub code_lens: CodeLens, } #[derive(Debug, Clone)] pub struct Jupyter { @@ -73,6 +74,12 @@ pub struct StickyScroll { pub enabled: bool, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct CodeLens { + pub enabled: bool, + pub debounce: DelayMs, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Toolbar { pub breadcrumbs: bool, @@ -199,6 +206,7 @@ impl Settings for EditorSettings { let search = editor.search.unwrap(); let drag_and_drop_selection = editor.drag_and_drop_selection.unwrap(); let sticky_scroll = editor.sticky_scroll.unwrap(); + let code_lens = editor.code_lens.unwrap(); Self { cursor_blink: editor.cursor_blink.unwrap(), cursor_shape: editor.cursor_shape.map(Into::into), @@ -289,6 +297,10 @@ impl Settings for EditorSettings { minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap().0, completion_menu_scrollbar: editor.completion_menu_scrollbar.map(Into::into).unwrap(), completion_detail_alignment: editor.completion_detail_alignment.unwrap(), + code_lens: CodeLens { + enabled: code_lens.enabled.unwrap(), + debounce: code_lens.debounce.unwrap(), + }, } } } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index e3e805cf91bd26..2dac1ff77b6aac 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -498,6 +498,7 @@ impl EditorElement { register_action(editor, window, Editor::toggle_relative_line_numbers); register_action(editor, window, Editor::toggle_indent_guides); register_action(editor, window, Editor::toggle_inlay_hints); + register_action(editor, window, Editor::toggle_code_lenses); register_action(editor, window, Editor::toggle_edit_predictions); if editor.read(cx).diagnostics_enabled() { register_action(editor, window, Editor::toggle_diagnostics); diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index c862a43cf95911..2a5ce2660f4a97 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -672,6 +672,7 @@ impl Editor { editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx); editor.update_lsp_data(None, window, cx); editor.colorize_brackets(false, cx); + editor.refresh_code_lenses(window, cx); }) .ok(); }); diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 9117da8ad49831..aae3f9341494ed 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -32,6 +32,7 @@ use util::rel_path::RelPath; use util::{ResultExt, maybe}; use crate::language_settings::language_settings; +use project::lsp_store::language_server_settings; pub struct RustLspAdapter; @@ -243,6 +244,55 @@ impl ManifestProvider for CargoManifestProvider { } } +fn set_experimental_capabilities(params: &mut InitializeParams, enable_lsp_tasks: bool) { + let mut experimental = json!({ + "commands": { + "commands": [ + "rust-analyzer.showReferences", + ] + } + }); + + if enable_lsp_tasks { + merge_json_value_into( + json!({ + "runnables": { + "kinds": [ "cargo", "shell" ], + }, + }), + &mut experimental, + ); + } + + if let Some(original_experimental) = &mut params.capabilities.experimental { + merge_json_value_into(experimental, original_experimental); + } else { + params.capabilities.experimental = Some(experimental); + } +} + +fn set_initialization_options(params: &mut InitializeParams) { + let lens_config = json!({ + "lens": { + "enable": true, + "implementations": { "enable": true }, + "references": { + "adt": { "enable": true }, + "adt.field": { "enable": true }, + "enumVariant": { "enable": true }, + "method": { "enable": true }, + "trait": { "enable": true } + } + } + }); + + if let Some(init_options) = &mut params.initialization_options { + merge_json_value_into(lens_config, init_options); + } else { + params.initialization_options = Some(lens_config); + } +} + #[async_trait(?Send)] impl LspAdapter for RustLspAdapter { fn name(&self) -> LanguageServerName { @@ -608,20 +658,50 @@ impl LspAdapter for RustLspAdapter { .lsp .get(&SERVER_NAME) .is_some_and(|s| s.enable_lsp_tasks); - if enable_lsp_tasks { - let experimental = json!({ - "runnables": { - "kinds": [ "cargo", "shell" ], - }, - }); - if let Some(original_experimental) = &mut original.capabilities.experimental { - merge_json_value_into(experimental, original_experimental); - } else { - original.capabilities.experimental = Some(experimental); + + set_experimental_capabilities(&mut original, enable_lsp_tasks); + set_initialization_options(&mut original); + + Ok(original) + } + + async fn workspace_configuration( + self: Arc, + delegate: &Arc, + _: Option, + _: Option, + cx: &mut AsyncApp, + ) -> Result { + let user_settings = cx.update(|cx| { + language_server_settings(delegate.as_ref(), &SERVER_NAME, cx) + .and_then(|s| s.settings.clone()) + }); + + let mut default_config = serde_json::json!({ + "lens": { + "enable": true, + "forceCustomCommands": false, + "run": { "enable": true }, + "debug": { "enable": true }, + "implementations": { "enable": true }, + "references": { + "adt": { "enable": true }, + "adt.field": { "enable": true }, + "enumVariant": { "enable": true }, + "method": { "enable": true }, + "trait": { "enable": true } + } } + }); + + if let Some(override_settings) = user_settings { + merge_json_value_into(override_settings, &mut default_config); } - Ok(original) + let config = serde_json::json!({ + "rust-analyzer": default_config + }); + Ok(config) } } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 9410697d03dd10..5738d52334cdcd 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -1056,6 +1056,7 @@ impl LocalLspStore { let mut cx = cx.clone(); async move { this.update(&mut cx, |this, cx| { + this.invalidate_code_lens_cache_for_server(server_id); cx.emit(LspStoreEvent::RefreshCodeLens); this.downstream_client.as_ref().map(|(client, project_id)| { client.send(proto::RefreshCodeLens { @@ -6081,6 +6082,7 @@ impl LspStore { ) -> CodeLensTask { let version_queried_for = buffer.read(cx).version(); let buffer_id = buffer.read(cx).remote_id(); + let existing_servers = self.as_local().map(|local| { local .buffers_opened_in_servers @@ -6148,19 +6150,23 @@ impl LspStore { .update(cx, |lsp_store, _| { let lsp_data = lsp_store.current_lsp_data(buffer_id)?; let code_lens = lsp_data.code_lens.as_mut()?; - if let Some(fetched_lens) = fetched_lens { + if let Some(fetched_lens) = &fetched_lens { if lsp_data.buffer_version == query_version_queried_for { - code_lens.lens.extend(fetched_lens); + code_lens.lens.extend(fetched_lens.clone()); } else if !lsp_data .buffer_version .changed_since(&query_version_queried_for) { lsp_data.buffer_version = query_version_queried_for; - code_lens.lens = fetched_lens; + code_lens.lens = fetched_lens.clone(); } + code_lens.update = None; + Some(code_lens.lens.values().flatten().cloned().collect()) + } else { + code_lens.update = None; + lsp_data.code_lens = None; + None } - code_lens.update = None; - Some(code_lens.lens.values().flatten().cloned().collect()) }) .map_err(Arc::new) }) @@ -6231,8 +6237,34 @@ impl LspStore { } else { let code_lens_actions_task = self.request_multiple_lsp_locally(buffer, None::, GetCodeLens, cx); - cx.background_spawn(async move { - Ok(Some(code_lens_actions_task.await.into_iter().collect())) + cx.spawn(async move |lsp_store, cx| { + let result = code_lens_actions_task.await; + if result.is_empty() { + return Ok(None); + } + + let mut resolved_result: HashMap> = + HashMap::default(); + for (server_id, mut actions) in result { + let language_server = lsp_store.update(cx, |lsp_store, _| { + lsp_store + .as_local() + .and_then(|local| local.language_server_for_id(server_id)) + })?; + + if let Some(language_server) = language_server { + for action in &mut actions { + if let Err(e) = + LocalLspStore::try_resolve_code_action(&language_server, action) + .await + { + log::warn!("Failed to resolve code lens: {e:#}"); + } + } + } + resolved_result.insert(server_id, actions); + } + Ok(Some(resolved_result)) }) } } @@ -10205,13 +10237,15 @@ impl LspStore { cx: &mut Context, ) { if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) { - if let Some(work) = status.pending_work.remove(&token) - && !work.is_disk_based_diagnostics_progress - { - cx.emit(LspStoreEvent::RefreshInlayHints { - server_id: language_server_id, - request_id: None, - }); + if let Some(work) = status.pending_work.remove(&token) { + if !work.is_disk_based_diagnostics_progress { + self.invalidate_code_lens_cache_for_server(language_server_id); + cx.emit(LspStoreEvent::RefreshInlayHints { + server_id: language_server_id, + request_id: None, + }); + cx.emit(LspStoreEvent::RefreshCodeLens); + } } cx.notify(); } @@ -13306,6 +13340,18 @@ impl LspStore { } lsp_data } + + fn invalidate_code_lens_cache_for_server(&mut self, server_id: LanguageServerId) { + for lsp_data in self.lsp_data.values_mut() { + if let Some(code_lens) = &mut lsp_data.code_lens { + if code_lens.lens.remove(&server_id).is_some() { + if code_lens.lens.is_empty() { + lsp_data.code_lens = None; + } + } + } + } + } } // Registration with registerOptions as null, should fallback to true. diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3d7b1898716968..3bf24267d5b84d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4115,11 +4115,11 @@ impl Project { range .start .cmp(&code_lens_action.range.start, &snapshot) - .is_ge() + .is_le() && range .end .cmp(&code_lens_action.range.end, &snapshot) - .is_le() + .is_ge() }); } Ok(code_lens_actions) diff --git a/crates/settings/src/vscode_import.rs b/crates/settings/src/vscode_import.rs index de8e266dd5581c..ee88d56f21c786 100644 --- a/crates/settings/src/vscode_import.rs +++ b/crates/settings/src/vscode_import.rs @@ -303,6 +303,7 @@ impl VsCodeSettings { vertical_scroll_margin: self.read_f32("editor.cursorSurroundingLines"), completion_menu_scrollbar: None, completion_detail_alignment: None, + code_lens: None, } } diff --git a/crates/settings_content/src/editor.rs b/crates/settings_content/src/editor.rs index 51b80fc92d8da2..3905529a637457 100644 --- a/crates/settings_content/src/editor.rs +++ b/crates/settings_content/src/editor.rs @@ -221,6 +221,9 @@ pub struct EditorSettingsContent { /// /// Default: left pub completion_detail_alignment: Option, + + /// Code lens settings for showing reference counts and other metadata above code elements. + pub code_lens: Option, } #[derive( @@ -827,6 +830,21 @@ pub struct DragAndDropSelectionContent { pub delay: Option, } +/// Code lens settings +#[with_fallible_options] +#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, PartialEq, Eq)] +pub struct CodeLensContent { + /// Whether code lens is enabled. + /// + /// Default: true + pub enabled: Option, + + /// The debounce delay before querying code lens from the language server. + /// + /// Default: 300 + pub debounce: Option, +} + /// When to show the minimap in the editor. /// /// Default: never