diff --git a/crates/ui/src/input/lsp/definitions.rs b/crates/ui/src/input/lsp/definitions.rs index 9536603c28..f542b5cdfc 100644 --- a/crates/ui/src/input/lsp/definitions.rs +++ b/crates/ui/src/input/lsp/definitions.rs @@ -111,7 +111,7 @@ impl InputState { pub(crate) fn on_action_go_to_definition( &mut self, _: &GoToDefinition, - _: &mut Window, + window: &mut Window, cx: &mut Context, ) { let offset = self.cursor(); @@ -121,7 +121,7 @@ impl InputState { } if let Some(location) = locations.first().cloned() { - self.go_to_definition(&location, cx); + self.go_to_definition(&location, window, cx); } } } @@ -131,7 +131,7 @@ impl InputState { &mut self, event: &MouseDownEvent, offset: usize, - _: &mut Window, + window: &mut Window, cx: &mut Context, ) -> bool { if !event.modifiers.secondary() { @@ -149,7 +149,7 @@ impl InputState { return false; }; - self.go_to_definition(&location, cx); + self.go_to_definition(&location, window, cx); true } @@ -157,18 +157,34 @@ impl InputState { pub(crate) fn go_to_definition( &mut self, location: &lsp_types::LocationLink, + window: &mut Window, cx: &mut Context, ) { - if location + let external = location .target_uri .scheme() .map(|s| s.as_str() == "https" || s.as_str() == "http") - == Some(true) - { + == Some(true); + + // Give the host a chance to show the document first (window/showDocument), + // e.g. to open virtual/external documents (stdlib docs) in an app window. + if let Some(handler) = self.lsp.show_document.clone() { + let params = lsp_types::ShowDocumentParams { + uri: location.target_uri.clone(), + external: Some(external), + take_focus: Some(true), + selection: Some(location.target_selection_range), + }; + if handler(¶ms, window, cx) { + return; + } + } + + if external { cx.open_url(&location.target_uri.to_string()); } else { // Move to the location. - let target_range = location.target_range; + let target_range = location.target_selection_range; let start = self.text.position_to_offset(&target_range.start); let end = self.text.position_to_offset(&target_range.end); diff --git a/crates/ui/src/input/lsp/mod.rs b/crates/ui/src/input/lsp/mod.rs index e42ac6d057..482771fe6b 100644 --- a/crates/ui/src/input/lsp/mod.rs +++ b/crates/ui/src/input/lsp/mod.rs @@ -19,6 +19,18 @@ pub use document_colors::*; pub use hover::*; pub use semantic_tokens::*; +/// Host hook to show a document when following an LSP location +/// (Go to Definition), modeled after the `window/showDocument` request. +/// +/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_showDocument +/// +/// Called before the built-in behavior. Return `true` if the host has shown +/// the document (e.g. opened a docs window for a virtual/external URI); +/// return `false` to fall through to the default handling (`external` URIs +/// open in the browser, anything else jumps within the current document). +pub type ShowDocumentHandler = + Rc bool>; + /// LSP ServerCapabilities /// /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#serverCapabilities @@ -35,6 +47,11 @@ pub struct Lsp { pub document_color_provider: Option>, /// The range semantic tokens provider. pub semantic_tokens_provider: Option>, + /// Optional host hook to show documents for Go to Definition locations, + /// following the `window/showDocument` request (see [`ShowDocumentHandler`]). + /// + /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_showDocument + pub show_document: Option, document_colors: Vec<(lsp_types::Range, Hsla)>, /// Cached semantic tokens as absolute position ranges + theme token-type @@ -55,6 +72,7 @@ impl Default for Lsp { definition_provider: None, document_color_provider: None, semantic_tokens_provider: None, + show_document: None, document_colors: vec![], semantic_tokens: vec![], _hover_task: Task::ready(Ok(())),