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:
Marc Schreiber 2024-01-11 22:24:49 +01:00 committed by GitHub
parent 0ebbc8f71c
commit 41119d3f88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 22 deletions

View File

@ -73,7 +73,7 @@ mod tests {
use lsp_types::Url;
use nu_test_support::fs::fixtures;
use crate::tests::{initialize_language_server, open, update};
use crate::tests::{initialize_language_server, open_unchecked, update};
#[test]
fn publish_diagnostics_variable_does_not_exists() {
@ -85,7 +85,7 @@ mod tests {
script.push("var.nu");
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!(
notification,
@ -116,7 +116,7 @@ mod tests {
script.push("var.nu");
let script = Url::from_file_path(script).unwrap();
open(&client_connection, script.clone());
open_unchecked(&client_connection, script.clone());
let notification = update(
&client_connection,
script.clone(),

View File

@ -180,7 +180,7 @@ impl LanguageServer {
}
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 start = lsp_types::Position {
@ -188,7 +188,7 @@ impl LanguageServer {
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 end = lsp_types::Position {
@ -704,8 +704,16 @@ mod tests {
assert_json_eq!(result, serde_json::json!(null));
}
pub fn open(client_connection: &Connection, uri: Url) -> lsp_server::Notification {
let text = std::fs::read_to_string(uri.to_file_path().unwrap()).unwrap();
pub fn open_unchecked(client_connection: &Connection, uri: Url) -> lsp_server::Notification {
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
.sender
@ -721,17 +729,17 @@ mod tests {
})
.unwrap(),
}))
.unwrap();
.map_err(|e| e.to_string())?;
let notification = client_connection
.receiver
.recv_timeout(Duration::from_secs(2))
.unwrap();
.map_err(|e| e.to_string())?;
if let Message::Notification(n) = notification {
n
Ok(n)
} else {
panic!();
Err(String::from("Did not receive a notification from server"))
}
}
@ -813,7 +821,7 @@ mod tests {
script.push("var.nu");
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 result = if let Message::Response(response) = resp {
@ -844,7 +852,7 @@ mod tests {
script.push("command.nu");
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 result = if let Message::Response(response) = resp {
@ -875,7 +883,7 @@ mod tests {
script.push("command.nu");
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 result = if let Message::Response(response) = resp {
@ -929,7 +937,7 @@ mod tests {
script.push("var.nu");
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 result = if let Message::Response(response) = resp {
@ -956,7 +964,7 @@ mod tests {
script.push("command.nu");
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 result = if let Message::Response(response) = resp {
@ -1011,7 +1019,7 @@ mod tests {
script.push("var.nu");
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 result = if let Message::Response(response) = resp {
@ -1047,7 +1055,7 @@ mod tests {
script.push("command.nu");
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 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"
}
}
])
);
}
}

View File

@ -95,7 +95,7 @@ mod tests {
use lsp_types::{Range, Url};
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]
fn hover_correct_documentation_on_let() {
@ -107,7 +107,7 @@ mod tests {
script.push("var.nu");
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 result = if let Message::Response(response) = resp {
@ -137,7 +137,7 @@ mod tests {
script.push("command.nu");
let script = Url::from_file_path(script).unwrap();
open(&client_connection, script.clone());
open_unchecked(&client_connection, script.clone());
update(
&client_connection,
script.clone(),
@ -178,7 +178,7 @@ hello"#,
script.push("command.nu");
let script = Url::from_file_path(script).unwrap();
open(&client_connection, script.clone());
open_unchecked(&client_connection, script.clone());
update(
&client_connection,
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(()))
}
}

View File

@ -0,0 +1 @@
" è " | str t

View 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 {