diff --git a/sway-lsp/benches/lsp_benchmarks/requests.rs b/sway-lsp/benches/lsp_benchmarks/requests.rs index cc6fae96f58..82a80a2a366 100644 --- a/sway-lsp/benches/lsp_benchmarks/requests.rs +++ b/sway-lsp/benches/lsp_benchmarks/requests.rs @@ -3,7 +3,9 @@ use lsp_types::{ CompletionResponse, DocumentSymbolResponse, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier, }; -use sway_lsp::{capabilities, lsp_ext::OnEnterParams, utils::keyword_docs::KeywordDocs}; +use sway_lsp::{ + capabilities, config::LspClient, lsp_ext::OnEnterParams, utils::keyword_docs::KeywordDocs, +}; use tokio::runtime::Runtime; fn benchmarks(c: &mut Criterion) { @@ -37,7 +39,15 @@ fn benchmarks(c: &mut Criterion) { }); c.bench_function("hover", |b| { - b.iter(|| capabilities::hover::hover_data(session.clone(), &keyword_docs, &uri, position)) + b.iter(|| { + capabilities::hover::hover_data( + session.clone(), + &keyword_docs, + &uri, + position, + LspClient::default(), + ) + }) }); c.bench_function("highlight", |b| { diff --git a/sway-lsp/src/capabilities/hover/mod.rs b/sway-lsp/src/capabilities/hover/mod.rs index 26c7a5e0172..97ef886daf7 100644 --- a/sway-lsp/src/capabilities/hover/mod.rs +++ b/sway-lsp/src/capabilities/hover/mod.rs @@ -1,5 +1,7 @@ pub(crate) mod hover_link_contents; +use self::hover_link_contents::HoverLinkContents; +use crate::config::LspClient; use crate::{ core::{ session::Session, @@ -9,23 +11,21 @@ use crate::{ attributes::doc_comment_attributes, keyword_docs::KeywordDocs, markdown, markup::Markup, }, }; +use lsp_types::{self, Position, Url}; use std::sync::Arc; use sway_core::{ language::{ty, Visibility}, Engines, TypeId, }; - -use lsp_types::{self, Position, Url}; use sway_types::{Span, Spanned}; -use self::hover_link_contents::HoverLinkContents; - /// Extracts the hover information for a token at the current position. pub fn hover_data( session: Arc, keyword_docs: &KeywordDocs, url: &Url, position: Position, + client_config: LspClient, ) -> Option { let t = session.token_map().token_at_position(url, position)?; let (ident, token) = t.pair(); @@ -60,11 +60,18 @@ pub fn hover_data( &session.engines.read(), decl_token, &decl_ident.name, + client_config.clone(), ) } // The `TypeInfo` of the token does not contain an `Ident`. In this case, // we use the `Ident` of the token itself. - None => hover_format(session.clone(), &session.engines.read(), token, &ident.name), + None => hover_format( + session.clone(), + &session.engines.read(), + token, + &ident.name, + client_config.clone(), + ), }; Some(lsp_types::Hover { @@ -122,6 +129,7 @@ fn hover_format( engines: &Engines, token: &Token, ident_name: &str, + client_config: LspClient, ) -> lsp_types::HoverContents { let decl_engine = engines.de(); let doc_comment = format_doc_attributes(engines, token); @@ -219,6 +227,7 @@ fn hover_format( engines.se(), &hover_link_contents.related_types, &hover_link_contents.implementations, + client_config, ); lsp_types::HoverContents::Markup(markup_content(&content)) diff --git a/sway-lsp/src/config.rs b/sway-lsp/src/config.rs index 0a3a5597462..7a440afeb57 100644 --- a/sway-lsp/src/config.rs +++ b/sway-lsp/src/config.rs @@ -4,6 +4,8 @@ use tracing::metadata::LevelFilter; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct Config { + #[serde(default)] + pub client: LspClient, #[serde(default)] pub debug: DebugConfig, #[serde(default)] @@ -20,6 +22,15 @@ pub struct Config { pub garbage_collection: GarbageCollectionConfig, } +#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum LspClient { + VsCode, + #[serde(other)] + #[default] + Other, +} + #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Default)] struct TraceConfig {} diff --git a/sway-lsp/src/handlers/request.rs b/sway-lsp/src/handlers/request.rs index d6d53a54a26..dd94d823959 100644 --- a/sway-lsp/src/handlers/request.rs +++ b/sway-lsp/src/handlers/request.rs @@ -132,6 +132,7 @@ pub async fn handle_hover( &state.keyword_docs, &uri, position, + state.config.read().client.clone(), )) } Err(err) => { diff --git a/sway-lsp/src/utils/markup.rs b/sway-lsp/src/utils/markup.rs index 9510674f435..0d2d641e4ea 100644 --- a/sway-lsp/src/utils/markup.rs +++ b/sway-lsp/src/utils/markup.rs @@ -4,8 +4,8 @@ //! markdown for this purpose. //! Modified from rust-analyzer. use crate::{ - capabilities::hover::hover_link_contents::RelatedType, core::token::get_range_from_span, - utils::document::get_url_from_span, + capabilities::hover::hover_link_contents::RelatedType, config::LspClient, + core::token::get_range_from_span, utils::document::get_url_from_span, }; use serde_json::{json, Value}; use std::fmt::{self}; @@ -56,13 +56,18 @@ impl Markup { } /// Adds go-to links if there are any related types, a link to view implementations if there are any, - /// or nothing if there are no related types or implementations. + /// or nothing if there are no related types or implementations. Only adds links for VSCode clients. pub fn maybe_add_links( self, source_engine: &SourceEngine, related_types: &[RelatedType], implementations: &[Span], + client_config: LspClient, ) -> Self { + if client_config != LspClient::VsCode { + return self; + } + if related_types.is_empty() { let locations = implementations .iter() diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 77727dbeb1a..d4187a182f5 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -4,6 +4,7 @@ use crate::integration::{code_actions, lsp}; use lsp_types::*; use std::{fs, path::PathBuf}; use sway_lsp::{ + config::LspClient, handlers::{notification, request}, server_state::ServerState, }; @@ -1635,9 +1636,10 @@ fn hover_docs_for_consts() { } #[test] -fn hover_docs_for_functions() { +fn hover_docs_for_functions_vscode() { run_async!({ let server = ServerState::default(); + server.config.write().client = LspClient::VsCode; let uri = open( &server, test_fixtures_dir().join("tokens/functions/src/main.sw"), @@ -1779,17 +1781,18 @@ fn hover_docs_with_code_examples() { } #[test] -fn hover_docs_for_self_keywords() { +fn hover_docs_for_self_keywords_vscode() { run_async!({ let server = ServerState::default(); + server.config.write().client = LspClient::VsCode; let uri = open(&server, test_fixtures_dir().join("completion/src/main.sw")).await; let mut hover = HoverDocumentation { - req_uri: &uri, - req_line: 11, - req_char: 13, - documentation: vec!["\n```sway\nself\n```\n\n---\n\n The receiver of a method, or the current module.\n\n `self` is used in two situations: referencing the current module and marking\n the receiver of a method.\n\n In paths, `self` can be used to refer to the current module, either in a\n [`use`] statement or in a path to access an element:\n\n ```sway\n use std::contract_id::{self, ContractId};\n ```\n\n Is functionally the same as:\n\n ```sway\n use std::contract_id;\n use std::contract_id::ContractId;\n ```\n\n `self` as the current receiver for a method allows to omit the parameter\n type most of the time. With the exception of this particularity, `self` is\n used much like any other parameter:\n\n ```sway\n struct Foo(u32);\n\n impl Foo {\n // No `self`.\n fn new() -> Self {\n Self(0)\n }\n\n // Borrowing `self`.\n fn value(&self) -> u32 {\n self.0\n }\n\n // Updating `self` mutably.\n fn clear(ref mut self) {\n self.0 = 0\n }\n }\n ```"], - }; + req_uri: &uri, + req_line: 11, + req_char: 13, + documentation: vec!["\n```sway\nself\n```\n\n---\n\n The receiver of a method, or the current module.\n\n `self` is used in two situations: referencing the current module and marking\n the receiver of a method.\n\n In paths, `self` can be used to refer to the current module, either in a\n [`use`] statement or in a path to access an element:\n\n ```sway\n use std::contract_id::{self, ContractId};\n ```\n\n Is functionally the same as:\n\n ```sway\n use std::contract_id;\n use std::contract_id::ContractId;\n ```\n\n `self` as the current receiver for a method allows to omit the parameter\n type most of the time. With the exception of this particularity, `self` is\n used much like any other parameter:\n\n ```sway\n struct Foo(u32);\n\n impl Foo {\n // No `self`.\n fn new() -> Self {\n Self(0)\n }\n\n // Borrowing `self`.\n fn value(&self) -> u32 {\n self.0\n }\n\n // Updating `self` mutably.\n fn clear(ref mut self) {\n self.0 = 0\n }\n }\n ```"], + }; lsp::hover_request(&server, &hover).await; hover.req_char = 24; @@ -1799,6 +1802,24 @@ fn hover_docs_for_self_keywords() { }); } +#[test] +fn hover_docs_for_self_keywords() { + run_async!({ + let server = ServerState::default(); + let uri = open(&server, test_fixtures_dir().join("completion/src/main.sw")).await; + + let hover = HoverDocumentation { + req_uri: &uri, + req_line: 11, + req_char: 13, + documentation: vec!["\n```sway\nself\n```\n\n---\n\n The receiver of a method, or the current module.\n\n `self` is used in two situations: referencing the current module and marking\n the receiver of a method.\n\n In paths, `self` can be used to refer to the current module, either in a\n [`use`] statement or in a path to access an element:\n\n ```sway\n use std::contract_id::{self, ContractId};\n ```\n\n Is functionally the same as:\n\n ```sway\n use std::contract_id;\n use std::contract_id::ContractId;\n ```\n\n `self` as the current receiver for a method allows to omit the parameter\n type most of the time. With the exception of this particularity, `self` is\n used much like any other parameter:\n\n ```sway\n struct Foo(u32);\n\n impl Foo {\n // No `self`.\n fn new() -> Self {\n Self(0)\n }\n\n // Borrowing `self`.\n fn value(&self) -> u32 {\n self.0\n }\n\n // Updating `self` mutably.\n fn clear(ref mut self) {\n self.0 = 0\n }\n }\n ```"], + }; + + lsp::hover_request(&server, &hover).await; + let _ = server.shutdown_server(); + }); +} + #[test] fn hover_docs_for_boolean_keywords() { run_async!({