mirror of
https://github.com/nushell/nushell.git
synced 2025-04-14 08:18:17 +02:00
Fix "Char index out of bounds" Error (#11526)
# Description The code that converts Nushell's span into LSP line and character indices accidentally treated the span as character indices while they are byte indices. Fixes #11522. # User-Facing Changes None, just a bugfix.
This commit is contained in:
parent
0ebbc8f71c
commit
41119d3f88
@ -73,7 +73,7 @@ mod tests {
|
|||||||
use lsp_types::Url;
|
use lsp_types::Url;
|
||||||
use nu_test_support::fs::fixtures;
|
use nu_test_support::fs::fixtures;
|
||||||
|
|
||||||
use crate::tests::{initialize_language_server, open, update};
|
use crate::tests::{initialize_language_server, open_unchecked, update};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn publish_diagnostics_variable_does_not_exists() {
|
fn publish_diagnostics_variable_does_not_exists() {
|
||||||
@ -85,7 +85,7 @@ mod tests {
|
|||||||
script.push("var.nu");
|
script.push("var.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
let notification = open(&client_connection, script.clone());
|
let notification = open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
assert_json_eq!(
|
assert_json_eq!(
|
||||||
notification,
|
notification,
|
||||||
@ -116,7 +116,7 @@ mod tests {
|
|||||||
script.push("var.nu");
|
script.push("var.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
let notification = update(
|
let notification = update(
|
||||||
&client_connection,
|
&client_connection,
|
||||||
script.clone(),
|
script.clone(),
|
||||||
|
@ -180,7 +180,7 @@ impl LanguageServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn span_to_range(span: &Span, rope_of_file: &Rope, offset: usize) -> lsp_types::Range {
|
fn span_to_range(span: &Span, rope_of_file: &Rope, offset: usize) -> lsp_types::Range {
|
||||||
let line = rope_of_file.char_to_line(span.start - offset);
|
let line = rope_of_file.byte_to_line(span.start - offset);
|
||||||
let character = span.start - offset - rope_of_file.line_to_char(line);
|
let character = span.start - offset - rope_of_file.line_to_char(line);
|
||||||
|
|
||||||
let start = lsp_types::Position {
|
let start = lsp_types::Position {
|
||||||
@ -188,7 +188,7 @@ impl LanguageServer {
|
|||||||
character: character as u32,
|
character: character as u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
let line = rope_of_file.char_to_line(span.end - offset);
|
let line = rope_of_file.byte_to_line(span.end - offset);
|
||||||
let character = span.end - offset - rope_of_file.line_to_char(line);
|
let character = span.end - offset - rope_of_file.line_to_char(line);
|
||||||
|
|
||||||
let end = lsp_types::Position {
|
let end = lsp_types::Position {
|
||||||
@ -704,8 +704,16 @@ mod tests {
|
|||||||
assert_json_eq!(result, serde_json::json!(null));
|
assert_json_eq!(result, serde_json::json!(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(client_connection: &Connection, uri: Url) -> lsp_server::Notification {
|
pub fn open_unchecked(client_connection: &Connection, uri: Url) -> lsp_server::Notification {
|
||||||
let text = std::fs::read_to_string(uri.to_file_path().unwrap()).unwrap();
|
open(client_connection, uri).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(
|
||||||
|
client_connection: &Connection,
|
||||||
|
uri: Url,
|
||||||
|
) -> Result<lsp_server::Notification, String> {
|
||||||
|
let text =
|
||||||
|
std::fs::read_to_string(uri.to_file_path().unwrap()).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
client_connection
|
client_connection
|
||||||
.sender
|
.sender
|
||||||
@ -721,17 +729,17 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let notification = client_connection
|
let notification = client_connection
|
||||||
.receiver
|
.receiver
|
||||||
.recv_timeout(Duration::from_secs(2))
|
.recv_timeout(Duration::from_secs(2))
|
||||||
.unwrap();
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
if let Message::Notification(n) = notification {
|
if let Message::Notification(n) = notification {
|
||||||
n
|
Ok(n)
|
||||||
} else {
|
} else {
|
||||||
panic!();
|
Err(String::from("Did not receive a notification from server"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -813,7 +821,7 @@ mod tests {
|
|||||||
script.push("var.nu");
|
script.push("var.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = goto_definition(&client_connection, script.clone(), 2, 12);
|
let resp = goto_definition(&client_connection, script.clone(), 2, 12);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -844,7 +852,7 @@ mod tests {
|
|||||||
script.push("command.nu");
|
script.push("command.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = goto_definition(&client_connection, script.clone(), 4, 1);
|
let resp = goto_definition(&client_connection, script.clone(), 4, 1);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -875,7 +883,7 @@ mod tests {
|
|||||||
script.push("command.nu");
|
script.push("command.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = goto_definition(&client_connection, script.clone(), 1, 14);
|
let resp = goto_definition(&client_connection, script.clone(), 1, 14);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -929,7 +937,7 @@ mod tests {
|
|||||||
script.push("var.nu");
|
script.push("var.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = hover(&client_connection, script.clone(), 2, 0);
|
let resp = hover(&client_connection, script.clone(), 2, 0);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -956,7 +964,7 @@ mod tests {
|
|||||||
script.push("command.nu");
|
script.push("command.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = hover(&client_connection, script.clone(), 3, 0);
|
let resp = hover(&client_connection, script.clone(), 3, 0);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -1011,7 +1019,7 @@ mod tests {
|
|||||||
script.push("var.nu");
|
script.push("var.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = complete(&client_connection, script, 2, 9);
|
let resp = complete(&client_connection, script, 2, 9);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -1047,7 +1055,7 @@ mod tests {
|
|||||||
script.push("command.nu");
|
script.push("command.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = complete(&client_connection, script, 0, 8);
|
let resp = complete(&client_connection, script, 0, 8);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -1073,4 +1081,41 @@ mod tests {
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_command_with_utf_line() {
|
||||||
|
let (client_connection, _recv) = initialize_language_server();
|
||||||
|
|
||||||
|
let mut script = fixtures();
|
||||||
|
script.push("lsp");
|
||||||
|
script.push("completion");
|
||||||
|
script.push("utf_pipeline.nu");
|
||||||
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
|
let resp = complete(&client_connection, script, 0, 14);
|
||||||
|
let result = if let Message::Response(response) = resp {
|
||||||
|
response.result
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_json_eq!(
|
||||||
|
result,
|
||||||
|
serde_json::json!([
|
||||||
|
{
|
||||||
|
"label": "str trim",
|
||||||
|
"detail": "Trim whitespace or specific character.",
|
||||||
|
"textEdit": {
|
||||||
|
"range": {
|
||||||
|
"start": { "line": 0, "character": 9 },
|
||||||
|
"end": { "line": 0, "character": 14 },
|
||||||
|
},
|
||||||
|
"newText": "str trim"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ mod tests {
|
|||||||
use lsp_types::{Range, Url};
|
use lsp_types::{Range, Url};
|
||||||
use nu_test_support::fs::fixtures;
|
use nu_test_support::fs::fixtures;
|
||||||
|
|
||||||
use crate::tests::{hover, initialize_language_server, open, update};
|
use crate::tests::{hover, initialize_language_server, open, open_unchecked, update};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hover_correct_documentation_on_let() {
|
fn hover_correct_documentation_on_let() {
|
||||||
@ -107,7 +107,7 @@ mod tests {
|
|||||||
script.push("var.nu");
|
script.push("var.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
|
|
||||||
let resp = hover(&client_connection, script.clone(), 0, 0);
|
let resp = hover(&client_connection, script.clone(), 0, 0);
|
||||||
let result = if let Message::Response(response) = resp {
|
let result = if let Message::Response(response) = resp {
|
||||||
@ -137,7 +137,7 @@ mod tests {
|
|||||||
script.push("command.nu");
|
script.push("command.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
update(
|
update(
|
||||||
&client_connection,
|
&client_connection,
|
||||||
script.clone(),
|
script.clone(),
|
||||||
@ -178,7 +178,7 @@ hello"#,
|
|||||||
script.push("command.nu");
|
script.push("command.nu");
|
||||||
let script = Url::from_file_path(script).unwrap();
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
open(&client_connection, script.clone());
|
open_unchecked(&client_connection, script.clone());
|
||||||
update(
|
update(
|
||||||
&client_connection,
|
&client_connection,
|
||||||
script.clone(),
|
script.clone(),
|
||||||
@ -212,4 +212,19 @@ hello"#,
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn open_document_with_utf_char() {
|
||||||
|
let (client_connection, _recv) = initialize_language_server();
|
||||||
|
|
||||||
|
let mut script = fixtures();
|
||||||
|
script.push("lsp");
|
||||||
|
script.push("notifications");
|
||||||
|
script.push("issue_11522.nu");
|
||||||
|
let script = Url::from_file_path(script).unwrap();
|
||||||
|
|
||||||
|
let result = open(&client_connection, script);
|
||||||
|
|
||||||
|
assert_eq!(result.map(|_| ()), Ok(()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
1
tests/fixtures/lsp/completion/utf_pipeline.nu
vendored
Normal file
1
tests/fixtures/lsp/completion/utf_pipeline.nu
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
" è " | str t
|
6
tests/fixtures/lsp/notifications/issue_11522.nu
vendored
Normal file
6
tests/fixtures/lsp/notifications/issue_11522.nu
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
# Important to reproduce the crash
|
||||||
|
# use a non ascii char somewhere in comments: è
|
||||||
|
|
||||||
|
[a b c d] | filter {
|
Loading…
Reference in New Issue
Block a user