1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

fix(lsp): relative completions for bare import-mapped specifiers (#26137)

This commit is contained in:
Nayeem Rahman 2024-10-11 07:40:17 +01:00 committed by GitHub
parent ccdbeb433b
commit 94b588ce66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 117 additions and 105 deletions

View file

@ -200,15 +200,11 @@ pub async fn get_import_completions(
{ {
// completions for import map specifiers // completions for import map specifiers
Some(lsp::CompletionResponse::List(completion_list)) Some(lsp::CompletionResponse::List(completion_list))
} else if text.starts_with("./") } else if let Some(completion_list) =
|| text.starts_with("../") get_local_completions(specifier, &text, &range, resolver)
|| text.starts_with('/')
{ {
// completions for local relative modules // completions for local relative modules
Some(lsp::CompletionResponse::List(CompletionList { Some(lsp::CompletionResponse::List(completion_list))
is_incomplete: false,
items: get_local_completions(specifier, &text, &range, resolver)?,
}))
} else if !text.is_empty() { } else if !text.is_empty() {
// completion of modules from a module registry or cache // completion of modules from a module registry or cache
check_auto_config_registry( check_auto_config_registry(
@ -363,15 +359,15 @@ fn get_local_completions(
text: &str, text: &str,
range: &lsp::Range, range: &lsp::Range,
resolver: &LspResolver, resolver: &LspResolver,
) -> Option<Vec<lsp::CompletionItem>> { ) -> Option<CompletionList> {
if base.scheme() != "file" { if base.scheme() != "file" {
return None; return None;
} }
let parent = base.join(text).ok()?.join(".").ok()?; let parent = &text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
let resolved_parent = resolver let resolved_parent = resolver
.as_graph_resolver(Some(base)) .as_graph_resolver(Some(base))
.resolve( .resolve(
parent.as_str(), parent,
&Range { &Range {
specifier: base.clone(), specifier: base.clone(),
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),
@ -381,13 +377,10 @@ fn get_local_completions(
) )
.ok()?; .ok()?;
let resolved_parent_path = url_to_file_path(&resolved_parent).ok()?; let resolved_parent_path = url_to_file_path(&resolved_parent).ok()?;
let raw_parent =
&text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
if resolved_parent_path.is_dir() { if resolved_parent_path.is_dir() {
let cwd = std::env::current_dir().ok()?; let cwd = std::env::current_dir().ok()?;
let items = std::fs::read_dir(resolved_parent_path).ok()?; let entries = std::fs::read_dir(resolved_parent_path).ok()?;
Some( let items = entries
items
.filter_map(|de| { .filter_map(|de| {
let de = de.ok()?; let de = de.ok()?;
let label = de.path().file_name()?.to_string_lossy().to_string(); let label = de.path().file_name()?.to_string_lossy().to_string();
@ -395,7 +388,7 @@ fn get_local_completions(
if entry_specifier == *base { if entry_specifier == *base {
return None; return None;
} }
let full_text = format!("{raw_parent}{label}"); let full_text = format!("{parent}{label}");
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: *range, range: *range,
new_text: full_text.clone(), new_text: full_text.clone(),
@ -435,8 +428,11 @@ fn get_local_completions(
_ => None, _ => None,
} }
}) })
.collect(), .collect();
) Some(CompletionList {
is_incomplete: false,
items,
})
} else { } else {
None None
} }
@ -921,11 +917,11 @@ mod tests {
}, },
}, },
&Default::default(), &Default::default(),
); )
assert!(actual.is_some()); .unwrap();
let actual = actual.unwrap(); assert!(!actual.is_incomplete);
assert_eq!(actual.len(), 3); assert_eq!(actual.items.len(), 3);
for item in actual { for item in actual.items {
match item.text_edit { match item.text_edit {
Some(lsp::CompletionTextEdit::Edit(text_edit)) => { Some(lsp::CompletionTextEdit::Edit(text_edit)) => {
assert!(["./b", "./f.mjs", "./g.json"] assert!(["./b", "./f.mjs", "./g.json"]

View file

@ -1342,6 +1342,7 @@ fn lsp_import_map_import_completions() {
"/#/": "./src/", "/#/": "./src/",
"fs": "https://example.com/fs/index.js", "fs": "https://example.com/fs/index.js",
"std/": "https://example.com/std@0.123.0/", "std/": "https://example.com/std@0.123.0/",
"lib/": "./lib/",
}, },
"scopes": { "scopes": {
"file:///": { "file:///": {
@ -1364,17 +1365,18 @@ fn lsp_import_map_import_completions() {
"uri": uri, "uri": uri,
"languageId": "typescript", "languageId": "typescript",
"version": 1, "version": 1,
"text": "import * as a from \"/~/b.ts\";\nimport * as b from \"\"" "text": r#"
} import * as b from "";
import * as b from "/~/";
import * as b from "lib/";
"#,
},
})); }));
let res = client.get_completion( let res = client.get_completion(
&uri, &uri,
(1, 20), (1, 28),
json!({ json!({ "triggerKind": 2, "triggerCharacter": "\"" }),
"triggerKind": 2,
"triggerCharacter": "\""
}),
); );
assert_eq!( assert_eq!(
json!(res), json!(res),
@ -1409,6 +1411,13 @@ fn lsp_import_map_import_completions() {
"sortText": "std", "sortText": "std",
"insertText": "std", "insertText": "std",
"commitCharacters": ["\"", "'"], "commitCharacters": ["\"", "'"],
}, {
"label": "lib",
"kind": 19,
"detail": "(import map)",
"sortText": "lib",
"insertText": "lib",
"commitCharacters": ["\"", "'"],
}, { }, {
"label": "fs", "label": "fs",
"kind": 17, "kind": 17,
@ -1435,32 +1444,10 @@ fn lsp_import_map_import_completions() {
}) })
); );
client.write_notification(
"textDocument/didChange",
json!({
"textDocument": {
"uri": uri,
"version": 2
},
"contentChanges": [
{
"range": {
"start": { "line": 1, "character": 20 },
"end": { "line": 1, "character": 20 }
},
"text": "/~/"
}
]
}),
);
let res = client.get_completion( let res = client.get_completion(
uri, &uri,
(1, 23), (2, 31),
json!({ json!({ "triggerKind": 2, "triggerCharacter": "/" }),
"triggerKind": 2,
"triggerCharacter": "/"
}),
); );
assert_eq!( assert_eq!(
json!(res), json!(res),
@ -1475,15 +1462,44 @@ fn lsp_import_map_import_completions() {
"filterText": "/~/b.ts", "filterText": "/~/b.ts",
"textEdit": { "textEdit": {
"range": { "range": {
"start": { "line": 1, "character": 20 }, "start": { "line": 2, "character": 28 },
"end": { "line": 1, "character": 23 } "end": { "line": 2, "character": 31 },
}, },
"newText": "/~/b.ts" "newText": "/~/b.ts",
}, },
"commitCharacters": ["\"", "'"], "commitCharacters": ["\"", "'"],
} },
] ],
}) }),
);
let res = client.get_completion(
&uri,
(3, 32),
json!({ "triggerKind": 2, "triggerCharacter": "/" }),
);
assert_eq!(
json!(res),
json!({
"isIncomplete": false,
"items": [
{
"label": "b.ts",
"kind": 17,
"detail": "(local)",
"sortText": "1",
"filterText": "lib/b.ts",
"textEdit": {
"range": {
"start": { "line": 3, "character": 28 },
"end": { "line": 3, "character": 32 },
},
"newText": "lib/b.ts",
},
"commitCharacters": ["\"", "'"],
},
],
}),
); );
client.shutdown(); client.shutdown();