mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
fix(lsp): properly handle snippets on completions (#16274)
Fixes #15367
This commit is contained in:
parent
e6e2898190
commit
afcea6c233
9 changed files with 298 additions and 4 deletions
|
@ -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"] }
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -74,6 +74,7 @@ impl ReplLanguageServer {
|
|||
window: None,
|
||||
general: None,
|
||||
experimental: None,
|
||||
offset_encoding: None,
|
||||
},
|
||||
trace: None,
|
||||
workspace_folders: None,
|
||||
|
|
|
@ -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<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub include_inlay_enum_member_value_hints: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub allow_rename_of_import_path: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_import_file_exclude_patterns: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
|
|
@ -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 <A t\n}",
|
||||
}
|
||||
}),
|
||||
);
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
"textDocument/completion",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/a.tsx"
|
||||
},
|
||||
"position": {
|
||||
"line": 5,
|
||||
"character": 13,
|
||||
},
|
||||
"context": {
|
||||
"triggerKind": 1,
|
||||
}
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
|
||||
assert!(!list.is_incomplete);
|
||||
assert_eq!(
|
||||
json!(list),
|
||||
json!({
|
||||
"isIncomplete": false,
|
||||
"items": [
|
||||
{
|
||||
"label": "type",
|
||||
"kind": 5,
|
||||
"sortText": "11",
|
||||
"filterText": "type=\"$1\"",
|
||||
"insertText": "type=\"$1\"",
|
||||
"insertTextFormat": 2,
|
||||
"commitCharacters": [
|
||||
".",
|
||||
",",
|
||||
";",
|
||||
"("
|
||||
],
|
||||
"data": {
|
||||
"tsc": {
|
||||
"specifier": "file:///a/a.tsx",
|
||||
"position": 87,
|
||||
"name": "type",
|
||||
"useCodeSnippet": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
} else {
|
||||
panic!("unexpected completion response");
|
||||
}
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
"completionItem/resolve",
|
||||
json!({
|
||||
"label": "type",
|
||||
"kind": 5,
|
||||
"sortText": "11",
|
||||
"filterText": "type=\"$1\"",
|
||||
"insertText": "type=\"$1\"",
|
||||
"insertTextFormat": 2,
|
||||
"commitCharacters": [
|
||||
".",
|
||||
",",
|
||||
";",
|
||||
"("
|
||||
],
|
||||
"data": {
|
||||
"tsc": {
|
||||
"specifier": "file:///a/a.tsx",
|
||||
"position": 87,
|
||||
"name": "type",
|
||||
"useCodeSnippet": false
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
assert_eq!(
|
||||
maybe_res,
|
||||
Some(json!({
|
||||
"label": "type",
|
||||
"kind": 5,
|
||||
"detail": "(property) type: string",
|
||||
"documentation": {
|
||||
"kind": "markdown",
|
||||
"value": ""
|
||||
},
|
||||
"sortText": "11",
|
||||
"filterText": "type=\"$1\"",
|
||||
"insertText": "type=\"$1\"",
|
||||
"insertTextFormat": 2,
|
||||
"commitCharacters": [
|
||||
".",
|
||||
",",
|
||||
";",
|
||||
"("
|
||||
]
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_completions_no_snippet() {
|
||||
let mut client = init("initialize_params_no_snippet.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 <A t\n}",
|
||||
}
|
||||
}),
|
||||
);
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
"textDocument/completion",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/a.tsx"
|
||||
},
|
||||
"position": {
|
||||
"line": 5,
|
||||
"character": 13,
|
||||
},
|
||||
"context": {
|
||||
"triggerKind": 1,
|
||||
}
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
|
||||
assert!(!list.is_incomplete);
|
||||
assert_eq!(
|
||||
json!(list),
|
||||
json!({
|
||||
"isIncomplete": false,
|
||||
"items": [
|
||||
{
|
||||
"label": "type",
|
||||
"kind": 5,
|
||||
"sortText": "11",
|
||||
"commitCharacters": [
|
||||
".",
|
||||
",",
|
||||
";",
|
||||
"("
|
||||
],
|
||||
"data": {
|
||||
"tsc": {
|
||||
"specifier": "file:///a/a.tsx",
|
||||
"position": 87,
|
||||
"name": "type",
|
||||
"useCodeSnippet": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
} else {
|
||||
panic!("unexpected completion response");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_completions_registry() {
|
||||
let _g = http_server();
|
||||
|
|
|
@ -56,6 +56,11 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"completion": {
|
||||
"completionItem": {
|
||||
"snippetSupport": true
|
||||
}
|
||||
},
|
||||
"foldingRange": {
|
||||
"lineFoldingOnly": true
|
||||
},
|
||||
|
|
77
cli/tests/testdata/lsp/initialize_params_no_snippet.json
vendored
Normal file
77
cli/tests/testdata/lsp/initialize_params_no_snippet.json
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"processId": 0,
|
||||
"clientInfo": {
|
||||
"name": "test-harness",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"rootUri": null,
|
||||
"initializationOptions": {
|
||||
"enable": true,
|
||||
"cache": null,
|
||||
"certificateStores": null,
|
||||
"codeLens": {
|
||||
"implementations": true,
|
||||
"references": true,
|
||||
"test": true
|
||||
},
|
||||
"config": null,
|
||||
"importMap": null,
|
||||
"lint": true,
|
||||
"suggest": {
|
||||
"autoImports": true,
|
||||
"completeFunctionCalls": false,
|
||||
"names": true,
|
||||
"paths": true,
|
||||
"imports": {
|
||||
"hosts": {}
|
||||
}
|
||||
},
|
||||
"testing": {
|
||||
"args": [
|
||||
"--allow-all"
|
||||
],
|
||||
"enable": true
|
||||
},
|
||||
"tlsCertificate": null,
|
||||
"unsafelyIgnoreCertificateErrors": null,
|
||||
"unstable": false
|
||||
},
|
||||
"capabilities": {
|
||||
"textDocument": {
|
||||
"codeAction": {
|
||||
"codeActionLiteralSupport": {
|
||||
"codeActionKind": {
|
||||
"valueSet": [
|
||||
"quickfix",
|
||||
"refactor"
|
||||
]
|
||||
}
|
||||
},
|
||||
"isPreferredSupport": true,
|
||||
"dataSupport": true,
|
||||
"disabledSupport": true,
|
||||
"resolveSupport": {
|
||||
"properties": [
|
||||
"edit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"foldingRange": {
|
||||
"lineFoldingOnly": true
|
||||
},
|
||||
"synchronization": {
|
||||
"dynamicRegistration": true,
|
||||
"willSave": true,
|
||||
"willSaveWaitUntil": true,
|
||||
"didSave": true
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"configuration": true,
|
||||
"workspaceFolders": true
|
||||
},
|
||||
"experimental": {
|
||||
"testingApi": true
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue