diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 0dc9a95cb1..a441f4389b 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -3022,6 +3022,68 @@ mod tests { harness.run().await; } + #[tokio::test] + async fn test_completions_optional() { + let mut harness = LspTestHarness::new(vec![ + ("initialize_request.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ( + "did_open_notification_completion_optional.json", + LspResponse::None, + ), + ( + "completion_request_optional.json", + LspResponse::Request( + 2, + json!({ + "isIncomplete": false, + "items": [ + { + "label": "b?", + "kind": 5, + "sortText": "1", + "filterText": "b", + "insertText": "b", + "data": { + "tsc": { + "specifier": "file:///a/file.ts", + "position": 79, + "name": "b", + "useCodeSnippet": false + } + } + } + ] + }), + ), + ), + ( + "completion_resolve_request_optional.json", + LspResponse::Request( + 4, + json!({ + "label": "b?", + "kind": 5, + "detail": "(property) A.b?: string | undefined", + "documentation": { + "kind": "markdown", + "value": "" + }, + "sortText": "1", + "filterText": "b", + "insertText": "b" + }), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } + #[derive(Deserialize)] struct PerformanceAverages { averages: Vec, diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index e2c184e59c..3c56ea5a4b 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -1190,10 +1190,10 @@ impl CompletionEntry { } let text_edit = - if let (Some(text_span), Some(new_text)) = (range, insert_text) { + if let (Some(text_span), Some(new_text)) = (range, &insert_text) { let range = text_span.to_range(line_index); let insert_replace_edit = lsp::InsertReplaceEdit { - new_text, + new_text: new_text.clone(), insert: range, replace: range, }; @@ -1218,6 +1218,7 @@ impl CompletionEntry { preselect, text_edit, filter_text, + insert_text, detail, tags, data: Some(json!({ diff --git a/cli/tests/lsp/completion_request_optional.json b/cli/tests/lsp/completion_request_optional.json new file mode 100644 index 0000000000..5e86c33ff6 --- /dev/null +++ b/cli/tests/lsp/completion_request_optional.json @@ -0,0 +1,18 @@ +{ + "jsonrpc": "2.0", + "id": 2, + "method": "textDocument/completion", + "params": { + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 8, + "character": 4 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "." + } + } +} diff --git a/cli/tests/lsp/completion_resolve_request_optional.json b/cli/tests/lsp/completion_resolve_request_optional.json new file mode 100644 index 0000000000..ffa60b9197 --- /dev/null +++ b/cli/tests/lsp/completion_resolve_request_optional.json @@ -0,0 +1,20 @@ +{ + "jsonrpc": "2.0", + "id": 4, + "method": "completionItem/resolve", + "params": { + "label": "b?", + "kind": 5, + "sortText": "1", + "filterText": "b", + "insertText": "b", + "data": { + "tsc": { + "specifier": "file:///a/file.ts", + "position": 79, + "name": "b", + "useCodeSnippet": false + } + } + } +} diff --git a/cli/tests/lsp/did_open_notification_completion_optional.json b/cli/tests/lsp/did_open_notification_completion_optional.json new file mode 100644 index 0000000000..5e919c80ab --- /dev/null +++ b/cli/tests/lsp/did_open_notification_completion_optional.json @@ -0,0 +1,12 @@ +{ + "jsonrpc": "2.0", + "method": "textDocument/didOpen", + "params": { + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "interface A {\n b?: string;\n}\n\nconst o: A = {};\n\nfunction c(s: string) {}\n\nc(o.)" + } + } +}