diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6dfea32226..267048b271 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -105,7 +105,7 @@ text-size = "=1.1.0" text_lines = "=0.6.0" tokio = { version = "=1.21.1", features = ["full"] } tokio-util = "=0.7.4" -tower-lsp = "=0.17.0" +tower-lsp = { version = "=0.17.0", features = ["proposed"] } twox-hash = "=1.6.3" typed-arena = "=2.0.1" uuid = { version = "=1.1.2", features = ["v4", "serde"] } diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs index 79d2eee1b7..0e9c93e638 100644 --- a/cli/lsp/capabilities.rs +++ b/cli/lsp/capabilities.rs @@ -58,6 +58,7 @@ pub fn server_capabilities( ";".to_string(), "(".to_string(), ]), + completion_item: None, trigger_characters: Some(vec![ ".".to_string(), "\"".to_string(), @@ -140,5 +141,6 @@ pub fn server_capabilities( "denoConfigTasks": true, "testingApi":true, })), + inlay_hint_provider: None, } } diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 27f52653d3..98ba5afb59 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -20,6 +20,7 @@ pub const SETTINGS_SECTION: &str = "deno"; pub struct ClientCapabilities { pub code_action_disabled_support: bool, pub line_folding_only: bool, + pub snippet_support: bool, pub status_notification: bool, /// The client provides the `experimental.testingApi` capability, which is /// built around VSCode's testing API. It indicates that the server should @@ -393,6 +394,16 @@ impl Config { .as_ref() .and_then(|it| it.disabled_support) .unwrap_or(false); + self.client_capabilities.snippet_support = + if let Some(completion) = &text_document.completion { + completion + .completion_item + .as_ref() + .and_then(|it| it.snippet_support) + .unwrap_or(false) + } else { + false + }; } } diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index f442d3d13e..64c7adeb6d 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -786,6 +786,7 @@ impl Inner { Ok(InitializeResult { capabilities, server_info: Some(server_info), + offset_encoding: None, }) } @@ -1777,6 +1778,7 @@ impl Inner { }; let position = line_index.offset_tsc(params.text_document_position.position)?; + let use_snippets = self.config.client_capabilities.snippet_support; let req = tsc::RequestMethod::GetCompletions(( specifier.clone(), position, @@ -1792,10 +1794,12 @@ impl Inner { self.config.get_workspace_settings().suggest.auto_imports, ), include_completions_for_module_exports: Some(true), - include_completions_with_object_literal_method_snippets: Some(true), - include_completions_with_class_member_snippets: Some(true), + include_completions_with_object_literal_method_snippets: Some( + use_snippets, + ), + include_completions_with_class_member_snippets: Some(use_snippets), include_completions_with_insert_text: Some(true), - include_completions_with_snippet_text: Some(true), + include_completions_with_snippet_text: Some(use_snippets), jsx_attribute_completion_style: Some( tsc::JsxAttributeCompletionStyle::Auto, ), diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs index b49a284b7c..b6329205a8 100644 --- a/cli/lsp/repl.rs +++ b/cli/lsp/repl.rs @@ -74,6 +74,7 @@ impl ReplLanguageServer { window: None, general: None, experimental: None, + offset_encoding: None, }, trace: None, workspace_folders: None, diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index e198c4fb82..51dd74240c 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -2196,6 +2196,10 @@ impl CompletionEntry { || kind == Some(lsp::CompletionItemKind::METHOD)); let commit_characters = self.get_commit_characters(info, settings); let mut insert_text = self.insert_text.clone(); + let insert_text_format = match self.is_snippet { + Some(true) => Some(lsp::InsertTextFormat::SNIPPET), + _ => None, + }; let range = self.replacement_span.clone(); let mut filter_text = self.get_filter_text(); let mut tags = None; @@ -2262,6 +2266,7 @@ impl CompletionEntry { text_edit, filter_text, insert_text, + insert_text_format, detail, tags, commit_characters, @@ -2910,6 +2915,10 @@ pub struct UserPreferences { pub include_inlay_function_like_return_type_hints: Option, #[serde(skip_serializing_if = "Option::is_none")] pub include_inlay_enum_member_value_hints: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub allow_rename_of_import_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub auto_import_file_exclude_patterns: Option>, } #[derive(Debug, Serialize)] diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs index 130ffe7425..cc8625476e 100644 --- a/cli/tests/integration/lsp_tests.rs +++ b/cli/tests/integration/lsp_tests.rs @@ -3654,6 +3654,191 @@ fn lsp_completions_auto_import() { ); } +#[test] +fn lsp_completions_snippet() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/a.tsx", + "languageId": "typescriptreact", + "version": 1, + "text": "function A({ type }: { type: string }) {\n return type;\n}\n\nfunction B() {\n return