diff --git a/crates/nu-lsp/src/diagnostics.rs b/crates/nu-lsp/src/diagnostics.rs index a6126e02c0..78da896266 100644 --- a/crates/nu-lsp/src/diagnostics.rs +++ b/crates/nu-lsp/src/diagnostics.rs @@ -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(), diff --git a/crates/nu-lsp/src/lib.rs b/crates/nu-lsp/src/lib.rs index 51a60a7e4a..70c49371c8 100644 --- a/crates/nu-lsp/src/lib.rs +++ b/crates/nu-lsp/src/lib.rs @@ -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 { + 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" + } + } + ]) + ); + } } diff --git a/crates/nu-lsp/src/notification.rs b/crates/nu-lsp/src/notification.rs index 085baebcbd..8bfab92664 100644 --- a/crates/nu-lsp/src/notification.rs +++ b/crates/nu-lsp/src/notification.rs @@ -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(())) + } } diff --git a/tests/fixtures/lsp/completion/utf_pipeline.nu b/tests/fixtures/lsp/completion/utf_pipeline.nu new file mode 100644 index 0000000000..5a5a7ce4d2 --- /dev/null +++ b/tests/fixtures/lsp/completion/utf_pipeline.nu @@ -0,0 +1 @@ +" è " | str t diff --git a/tests/fixtures/lsp/notifications/issue_11522.nu b/tests/fixtures/lsp/notifications/issue_11522.nu new file mode 100644 index 0000000000..6bc1b59844 --- /dev/null +++ b/tests/fixtures/lsp/notifications/issue_11522.nu @@ -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 {