feat(lsp): completion items now respect the append_whitespace flag (#15247)

# Description

Append space if marked as required.
Aligned behavior as the REPL completion.

# User-Facing Changes

# Tests + Formatting

Adjusted

# After Submitting
This commit is contained in:
zc he 2025-03-05 19:45:27 +08:00 committed by GitHub
parent 4779d69de6
commit 49f92e9090
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -60,8 +60,12 @@ impl LanguageServer {
.then_some(engine_state.find_decl(r.suggestion.value.as_bytes(), &[])?) .then_some(engine_state.find_decl(r.suggestion.value.as_bytes(), &[])?)
}); });
let mut label_value = r.suggestion.value;
if r.suggestion.append_whitespace {
label_value.push(' ');
}
CompletionItem { CompletionItem {
label: r.suggestion.value.clone(), label: label_value.clone(),
label_details: r label_details: r
.kind .kind
.clone() .clone()
@ -98,7 +102,7 @@ impl LanguageServer {
start, start,
end: params.text_document_position.position, end: params.text_document_position.position,
}, },
new_text: r.suggestion.value, new_text: label_value,
})), })),
..Default::default() ..Default::default()
} }
@ -119,9 +123,9 @@ impl LanguageServer {
SuggestionKind::Command(c) => match c { SuggestionKind::Command(c) => match c {
CommandType::Keyword => Some(CompletionItemKind::KEYWORD), CommandType::Keyword => Some(CompletionItemKind::KEYWORD),
CommandType::Builtin => Some(CompletionItemKind::FUNCTION), CommandType::Builtin => Some(CompletionItemKind::FUNCTION),
CommandType::Custom => Some(CompletionItemKind::METHOD),
CommandType::Alias => Some(CompletionItemKind::REFERENCE), CommandType::Alias => Some(CompletionItemKind::REFERENCE),
CommandType::External | CommandType::Plugin => Some(CompletionItemKind::INTERFACE), CommandType::External => Some(CompletionItemKind::INTERFACE),
CommandType::Custom | CommandType::Plugin => Some(CompletionItemKind::METHOD),
}, },
SuggestionKind::Directory => Some(CompletionItemKind::FOLDER), SuggestionKind::Directory => Some(CompletionItemKind::FOLDER),
SuggestionKind::File => Some(CompletionItemKind::FILE), SuggestionKind::File => Some(CompletionItemKind::FILE),
@ -227,15 +231,15 @@ mod tests {
expected: serde_json::json!([ expected: serde_json::json!([
// defined after the cursor // defined after the cursor
{ {
"label": "config", "label": "config ",
"detail": "Edit nushell configuration files.", "detail": "Edit nushell configuration files.",
"textEdit": { "range": { "start": { "line": 0, "character": 0 }, "end": { "line": 0, "character": 6 }, }, "textEdit": { "range": { "start": { "line": 0, "character": 0 }, "end": { "line": 0, "character": 6 }, },
"newText": "config" "newText": "config "
}, },
}, },
{ "label": "config env", "kind": 3 }, { "label": "config env ", "kind": 3 },
{ "label": "config flatten", "kind": 3 }, { "label": "config flatten ", "kind": 3 },
{ "label": "config n foo bar", "detail": detail_str, "kind": 2 }, { "label": "config n foo bar ", "detail": detail_str, "kind": 2 },
]) ])
); );
@ -243,11 +247,11 @@ mod tests {
let resp = send_complete_request(&client_connection, script.clone(), 1, 18); let resp = send_complete_request(&client_connection, script.clone(), 1, 18);
assert!(result_from_message(resp).as_array().unwrap().contains( assert!(result_from_message(resp).as_array().unwrap().contains(
&serde_json::json!({ &serde_json::json!({
"label": "-s", "label": "-s ",
"detail": "test flag", "detail": "test flag",
"labelDetails": { "description": "flag" }, "labelDetails": { "description": "flag" },
"textEdit": { "range": { "start": { "line": 1, "character": 17 }, "end": { "line": 1, "character": 18 }, }, "textEdit": { "range": { "start": { "line": 1, "character": 17 }, "end": { "line": 1, "character": 18 }, },
"newText": "-s" "newText": "-s "
}, },
"kind": 5 "kind": 5
}) })
@ -257,11 +261,11 @@ mod tests {
let resp = send_complete_request(&client_connection, script.clone(), 2, 22); let resp = send_complete_request(&client_connection, script.clone(), 2, 22);
assert!(result_from_message(resp).as_array().unwrap().contains( assert!(result_from_message(resp).as_array().unwrap().contains(
&serde_json::json!({ &serde_json::json!({
"label": "--long", "label": "--long ",
"detail": "test flag", "detail": "test flag",
"labelDetails": { "description": "flag" }, "labelDetails": { "description": "flag" },
"textEdit": { "range": { "start": { "line": 2, "character": 19 }, "end": { "line": 2, "character": 22 }, }, "textEdit": { "range": { "start": { "line": 2, "character": 19 }, "end": { "line": 2, "character": 22 }, },
"newText": "--long" "newText": "--long "
}, },
"kind": 5 "kind": 5
}) })
@ -284,11 +288,11 @@ mod tests {
let resp = send_complete_request(&client_connection, script, 10, 34); let resp = send_complete_request(&client_connection, script, 10, 34);
assert!(result_from_message(resp).as_array().unwrap().contains( assert!(result_from_message(resp).as_array().unwrap().contains(
&serde_json::json!({ &serde_json::json!({
"label": "-g", "label": "-g ",
"detail": "count indexes and split using grapheme clusters (all visible chars have length 1)", "detail": "count indexes and split using grapheme clusters (all visible chars have length 1)",
"labelDetails": { "description": "flag" }, "labelDetails": { "description": "flag" },
"textEdit": { "range": { "start": { "line": 10, "character": 33 }, "end": { "line": 10, "character": 34 }, }, "textEdit": { "range": { "start": { "line": 10, "character": 33 }, "end": { "line": 10, "character": 34 }, },
"newText": "-g" "newText": "-g "
}, },
"kind": 5 "kind": 5
}) })
@ -312,12 +316,12 @@ mod tests {
actual: result_from_message(resp), actual: result_from_message(resp),
expected: serde_json::json!([ expected: serde_json::json!([
{ {
"label": "alias", "label": "alias ",
"labelDetails": { "description": "keyword" }, "labelDetails": { "description": "keyword" },
"detail": "Alias a command (with optional flags) to a new name.", "detail": "Alias a command (with optional flags) to a new name.",
"textEdit": { "textEdit": {
"range": { "start": { "line": 0, "character": 0 }, "end": { "line": 0, "character": 0 }, }, "range": { "start": { "line": 0, "character": 0 }, "end": { "line": 0, "character": 0 }, },
"newText": "alias" "newText": "alias "
}, },
"kind": 14 "kind": 14
} }
@ -329,12 +333,12 @@ mod tests {
actual: result_from_message(resp), actual: result_from_message(resp),
expected: serde_json::json!([ expected: serde_json::json!([
{ {
"label": "alias", "label": "alias ",
"labelDetails": { "description": "keyword" }, "labelDetails": { "description": "keyword" },
"detail": "Alias a command (with optional flags) to a new name.", "detail": "Alias a command (with optional flags) to a new name.",
"textEdit": { "textEdit": {
"range": { "start": { "line": 3, "character": 2 }, "end": { "line": 3, "character": 2 }, }, "range": { "start": { "line": 3, "character": 2 }, "end": { "line": 3, "character": 2 }, },
"newText": "alias" "newText": "alias "
}, },
"kind": 14 "kind": 14
} }
@ -371,12 +375,12 @@ mod tests {
actual: result_from_message(resp), actual: result_from_message(resp),
expected: serde_json::json!([ expected: serde_json::json!([
{ {
"label": "str trim", "label": "str trim ",
"labelDetails": { "description": "built-in" }, "labelDetails": { "description": "built-in" },
"detail": "Trim whitespace or specific character.", "detail": "Trim whitespace or specific character.",
"textEdit": { "textEdit": {
"range": { "start": { "line": 0, "character": 8 }, "end": { "line": 0, "character": 13 }, }, "range": { "start": { "line": 0, "character": 8 }, "end": { "line": 0, "character": 13 }, },
"newText": "str trim" "newText": "str trim "
}, },
"kind": 3 "kind": 3
} }
@ -401,10 +405,10 @@ mod tests {
actual: result_from_message(resp), actual: result_from_message(resp),
expected: serde_json::json!([ expected: serde_json::json!([
{ {
"label": "overlay", "label": "overlay ",
"labelDetails": { "description": "keyword" }, "labelDetails": { "description": "keyword" },
"textEdit": { "textEdit": {
"newText": "overlay", "newText": "overlay ",
"range": { "start": { "character": 0, "line": 0 }, "end": { "character": 2, "line": 0 } } "range": { "start": { "character": 0, "line": 0 }, "end": { "character": 2, "line": 0 } }
}, },
"kind": 14 "kind": 14
@ -490,12 +494,12 @@ mod tests {
actual: result_from_message(resp), actual: result_from_message(resp),
expected: serde_json::json!([ expected: serde_json::json!([
{ {
"label": "alias", "label": "alias ",
"labelDetails": { "description": "keyword" }, "labelDetails": { "description": "keyword" },
"detail": "Alias a command (with optional flags) to a new name.", "detail": "Alias a command (with optional flags) to a new name.",
"textEdit": { "textEdit": {
"range": { "start": { "line": 0, "character": 5 }, "end": { "line": 0, "character": 5 }, }, "range": { "start": { "line": 0, "character": 5 }, "end": { "line": 0, "character": 5 }, },
"newText": "alias" "newText": "alias "
}, },
"kind": 14 "kind": 14
}, },
@ -520,10 +524,10 @@ mod tests {
actual: result_from_message(resp), actual: result_from_message(resp),
expected: serde_json::json!([ expected: serde_json::json!([
{ {
"label": "!=", "label": "!= ",
"labelDetails": { "description": "operator" }, "labelDetails": { "description": "operator" },
"textEdit": { "textEdit": {
"newText": "!=", "newText": "!= ",
"range": { "start": { "character": 10, "line": 7 }, "end": { "character": 10, "line": 7 } } "range": { "start": { "character": 10, "line": 7 }, "end": { "character": 10, "line": 7 } }
}, },
"kind": 24 // operator kind "kind": 24 // operator kind
@ -536,10 +540,10 @@ mod tests {
actual: result_from_message(resp), actual: result_from_message(resp),
expected: serde_json::json!([ expected: serde_json::json!([
{ {
"label": "not-has", "label": "not-has ",
"labelDetails": { "description": "operator" }, "labelDetails": { "description": "operator" },
"textEdit": { "textEdit": {
"newText": "not-has", "newText": "not-has ",
"range": { "start": { "character": 10, "line": 7 }, "end": { "character": 15, "line": 7 } } "range": { "start": { "character": 10, "line": 7 }, "end": { "character": 15, "line": 7 } }
}, },
"kind": 24 // operator kind "kind": 24 // operator kind