feat(lsp): node specifier completions (#24904)

This commit is contained in:
Nayeem Rahman 2024-08-06 16:30:34 +01:00 committed by GitHub
parent b3f1f3ba04
commit 9a83efa04b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 143 additions and 1 deletions

View file

@ -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(

View file

@ -7498,6 +7498,111 @@ fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map() {
client.shutdown();
}
#[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();
@ -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()