diff options
-rw-r--r-- | cli/lsp/completions.rs | 37 | ||||
-rw-r--r-- | tests/integration/lsp_tests.rs | 107 |
2 files changed, 143 insertions, 1 deletions
diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs index a51edcb33..88900ceef 100644 --- a/cli/lsp/completions.rs +++ b/cli/lsp/completions.rs @@ -18,6 +18,7 @@ use crate::util::path::is_importable_ext; use crate::util::path::relative_specifier; use deno_graph::source::ResolutionMode; use deno_graph::Range; +use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES; use deno_runtime::fs_util::specifier_to_file_path; use deno_ast::LineAndColumnIndex; @@ -192,6 +193,8 @@ pub async fn get_import_completions( get_npm_completions(specifier, &text, &range, npm_search_api).await { Some(lsp::CompletionResponse::List(completion_list)) + } else if let Some(completion_list) = get_node_completions(&text, &range) { + Some(lsp::CompletionResponse::List(completion_list)) } else if let Some(completion_list) = get_import_map_completions(specifier, &text, &range, maybe_import_map) { @@ -732,6 +735,40 @@ async fn get_npm_completions( }) } +/// Get completions for `node:` specifiers. +fn get_node_completions( + specifier: &str, + range: &lsp::Range, +) -> Option<CompletionList> { + if !specifier.starts_with("node:") { + return None; + } + let items = SUPPORTED_BUILTIN_NODE_MODULES + .iter() + .map(|name| { + let specifier = format!("node:{}", name); + let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { + range: *range, + new_text: specifier.clone(), + })); + lsp::CompletionItem { + label: specifier, + kind: Some(lsp::CompletionItemKind::FILE), + detail: Some("(node)".to_string()), + text_edit, + commit_characters: Some( + IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(), + ), + ..Default::default() + } + }) + .collect(); + Some(CompletionList { + is_incomplete: false, + items, + }) +} + /// Get workspace completions that include modules in the Deno cache which match /// the current specifier string. fn get_workspace_completions( diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index cbc175ec6..d89061683 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -7499,6 +7499,111 @@ fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map() { } #[test] +fn lsp_completions_node_specifier() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.uri().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": "import fs from \"node:as\";\n", + }, + })); + let list = client.get_completion_list( + temp_dir.uri().join("file.ts").unwrap(), + (0, 23), + json!({ + "triggerKind": 2, + "triggerCharacter": ".", + }), + ); + assert!(!list.is_incomplete); + assert_eq!( + list + .items + .iter() + .map(|i| i.label.as_str()) + .collect::<Vec<_>>(), + vec![ + "node:assert", + "node:assert/strict", + "node:async_hooks", + "node:buffer", + "node:child_process", + "node:cluster", + "node:console", + "node:constants", + "node:crypto", + "node:dgram", + "node:diagnostics_channel", + "node:dns", + "node:dns/promises", + "node:domain", + "node:events", + "node:fs", + "node:fs/promises", + "node:http", + "node:http2", + "node:https", + "node:module", + "node:net", + "node:os", + "node:path", + "node:path/posix", + "node:path/win32", + "node:perf_hooks", + "node:process", + "node:punycode", + "node:querystring", + "node:repl", + "node:readline", + "node:readline/promises", + "node:stream", + "node:stream/consumers", + "node:stream/promises", + "node:stream/web", + "node:string_decoder", + "node:sys", + "node:test", + "node:timers", + "node:timers/promises", + "node:tls", + "node:tty", + "node:url", + "node:util", + "node:util/types", + "node:v8", + "node:vm", + "node:worker_threads", + "node:zlib", + ], + ); + for item in &list.items { + let specifier = item.label.as_str(); + assert_eq!( + json!(item), + json!({ + "label": specifier, + "kind": 17, + "detail": "(node)", + "textEdit": { + "range": { + "start": { "line": 0, "character": 16 }, + "end": { "line": 0, "character": 23 }, + }, + "newText": specifier, + }, + "commitCharacters": ["\"", "'"], + }), + ); + } + client.shutdown(); +} + +#[test] fn lsp_infer_return_type() { let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); @@ -8614,7 +8719,7 @@ fn lsp_npm_specifier_unopened_file() { } #[test] -fn lsp_completions_node_specifier() { +fn lsp_completions_node_builtin() { let context = TestContextBuilder::new() .use_http_server() .use_temp_cwd() |