Skip to content

Commit

Permalink
Implement LSP Text Document Synchronization (nushell#10941)
Browse files Browse the repository at this point in the history
  • Loading branch information
schrieveslaach authored and hardfau1t committed Dec 14, 2023
1 parent 27243a0 commit add2fc4
Show file tree
Hide file tree
Showing 8 changed files with 619 additions and 146 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/nu-lsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ nu-protocol = { path = "../nu-protocol", version = "0.87.1" }

reedline = { version = "0.26" }

crossbeam-channel = "0.5.8"
lsp-types = "0.94.1"
lsp-server = "0.7.4"
lsp-server = { version = "0.7.4", git = "https://github.com/schrieveslaach/rust-analyzer.git", branch = "cancelable-initialization" }
miette = "5.10"
ropey = "1.6.1"
serde = "1.0"
Expand Down
147 changes: 147 additions & 0 deletions crates/nu-lsp/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use lsp_types::{
notification::{Notification, PublishDiagnostics},
Diagnostic, DiagnosticSeverity, PublishDiagnosticsParams, Url,
};
use miette::{IntoDiagnostic, Result};
use nu_parser::parse;
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
eval_const::create_nu_constant,
Span, Value, NU_VARIABLE_ID,
};

use crate::LanguageServer;

impl LanguageServer {
pub(crate) fn publish_diagnostics_for_file(
&self,
uri: Url,
engine_state: &mut EngineState,
) -> Result<()> {
let cwd = std::env::current_dir().expect("Could not get current working directory.");
engine_state.add_env_var("PWD".into(), Value::test_string(cwd.to_string_lossy()));

let Ok(nu_const) = create_nu_constant(engine_state, Span::unknown()) else {
return Ok(());
};
engine_state.set_variable_const_val(NU_VARIABLE_ID, nu_const);

let mut working_set = StateWorkingSet::new(engine_state);

let Some((rope_of_file, file_path)) = self.rope(&uri) else {
return Ok(());
};

let contents = rope_of_file.bytes().collect::<Vec<u8>>();
let offset = working_set.next_span_start();
parse(
&mut working_set,
Some(&file_path.to_string_lossy()),
&contents,
false,
);

let mut diagnostics = PublishDiagnosticsParams {
uri,
diagnostics: Vec::new(),
version: None,
};

for err in working_set.parse_errors.iter() {
let message = err.to_string();

diagnostics.diagnostics.push(Diagnostic {
range: Self::span_to_range(&err.span(), rope_of_file, offset),
severity: Some(DiagnosticSeverity::ERROR),
message,
..Default::default()
});
}

self.connection
.sender
.send(lsp_server::Message::Notification(
lsp_server::Notification::new(PublishDiagnostics::METHOD.to_string(), diagnostics),
))
.into_diagnostic()
}
}

#[cfg(test)]
mod tests {
use assert_json_diff::assert_json_eq;
use lsp_types::Url;
use nu_test_support::fs::fixtures;

use crate::tests::{initialize_language_server, open, update};

#[test]
fn publish_diagnostics_variable_does_not_exists() {
let (client_connection, _recv) = initialize_language_server();

let mut script = fixtures();
script.push("lsp");
script.push("diagnostics");
script.push("var.nu");
let script = Url::from_file_path(script).unwrap();

let notification = open(&client_connection, script.clone());

assert_json_eq!(
notification,
serde_json::json!({
"method": "textDocument/publishDiagnostics",
"params": {
"uri": script,
"diagnostics": [{
"range": {
"start": { "line": 0, "character": 6 },
"end": { "line": 0, "character": 30 }
},
"message": "Variable not found.",
"severity": 1
}]
}
})
);
}

#[test]
fn publish_diagnostics_fixed_unknown_variable() {
let (client_connection, _recv) = initialize_language_server();

let mut script = fixtures();
script.push("lsp");
script.push("diagnostics");
script.push("var.nu");
let script = Url::from_file_path(script).unwrap();

open(&client_connection, script.clone());
let notification = update(
&client_connection,
script.clone(),
String::from("$env"),
Some(lsp_types::Range {
start: lsp_types::Position {
line: 0,
character: 6,
},
end: lsp_types::Position {
line: 0,
character: 30,
},
}),
);

assert_json_eq!(
notification,
serde_json::json!({
"method": "textDocument/publishDiagnostics",
"params": {
"uri": script,
"diagnostics": []
}
})
);
}
}

0 comments on commit add2fc4

Please sign in to comment.