mirror of
https://github.com/nushell/nushell.git
synced 2025-04-29 07:34:28 +02:00
fix(lsp): a panic caused by completion with decl_id out of range (#15576)
Fixes a bug caused by #15536 Sorry about that, @fdncred # Description I've made the panic reproducible in the test case. TLDR: completer will sometimes return new decl_ids outside of the range of the engine_state passed in. # User-Facing Changes bug fix # Tests + Formatting +1 # After Submitting
This commit is contained in:
parent
24cc2f9d87
commit
cd4560e97a
@ -69,7 +69,8 @@ impl Completer for ExportableCompletion<'_> {
|
||||
wrapped_name(name),
|
||||
Some(cmd.description().to_string()),
|
||||
None,
|
||||
SuggestionKind::Command(cmd.command_type(), Some(*decl_id)),
|
||||
// `None` here avoids arguments being expanded by snippet edit style for lsp
|
||||
SuggestionKind::Command(cmd.command_type(), None),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -68,13 +68,19 @@ impl LanguageServer {
|
||||
let mut idx = 0;
|
||||
// use snippet as `insert_text_format` for command argument completion
|
||||
if let Some(SuggestionKind::Command(_, Some(decl_id))) = suggestion.kind {
|
||||
// NOTE: for new commands defined in current context,
|
||||
// which are not present in the engine state, skip the documentation and snippet.
|
||||
if engine_state.num_decls() > decl_id.get() {
|
||||
let cmd = engine_state.get_decl(decl_id);
|
||||
doc_string = Some(Self::get_decl_description(cmd, true));
|
||||
insert_text_format = Some(InsertTextFormat::SNIPPET);
|
||||
let signature = cmd.signature();
|
||||
// add curly brackets around block arguments
|
||||
// and keywords, e.g. `=` in `alias foo = bar`
|
||||
let mut arg_wrapper = |arg: &PositionalArg, text: String, optional: bool| -> String {
|
||||
let mut arg_wrapper = |arg: &PositionalArg,
|
||||
text: String,
|
||||
optional: bool|
|
||||
-> String {
|
||||
idx += 1;
|
||||
match &arg.shape {
|
||||
SyntaxShape::Block | SyntaxShape::MatchBlock => {
|
||||
@ -108,14 +114,16 @@ impl LanguageServer {
|
||||
}
|
||||
for optional in signature.optional_positional {
|
||||
snippet_text.push(' ');
|
||||
snippet_text
|
||||
.push_str(arg_wrapper(&optional, format!("{}?", optional.name), true).as_str());
|
||||
snippet_text.push_str(
|
||||
arg_wrapper(&optional, format!("{}?", optional.name), true).as_str(),
|
||||
);
|
||||
}
|
||||
if let Some(rest) = signature.rest_positional {
|
||||
idx += 1;
|
||||
snippet_text.push_str(format!(" ${{{}:...{}}}", idx, rest.name).as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
// no extra space for a command with args expanded in the snippet
|
||||
if idx == 0 && suggestion.suggestion.append_whitespace {
|
||||
snippet_text.push(' ');
|
||||
@ -329,6 +337,17 @@ mod tests {
|
||||
})
|
||||
));
|
||||
|
||||
// fallback completion on a newly defined command,
|
||||
// the decl_id is missing in the engine state, this shouldn't cause any panic.
|
||||
let resp = send_complete_request(&client_connection, script.clone(), 13, 9);
|
||||
assert_json_include!(
|
||||
actual: result_from_message(resp),
|
||||
expected: serde_json::json!([
|
||||
// defined before the cursor
|
||||
{ "label": "config n foo bar", "detail": detail_str, "kind": 2 },
|
||||
])
|
||||
);
|
||||
|
||||
// inside parenthesis
|
||||
let resp = send_complete_request(&client_connection, script, 10, 34);
|
||||
assert!(result_from_message(resp).as_array().unwrap().contains(
|
||||
|
2
tests/fixtures/lsp/completion/command.nu
vendored
2
tests/fixtures/lsp/completion/command.nu
vendored
@ -10,3 +10,5 @@ def "config n foo bar" [
|
||||
echo "🤔🐘"
|
||||
| str substring (str substring -)
|
||||
}
|
||||
|
||||
config n # don't panic!
|
||||
|
Loading…
Reference in New Issue
Block a user