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::>(); 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_unchecked, 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_unchecked(&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_unchecked(&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": [] } }) ); } }