diff --git a/src/lspc.rs b/src/lspc.rs index 17b4e0e..79587f2 100644 --- a/src/lspc.rs +++ b/src/lspc.rs @@ -163,6 +163,8 @@ pub trait Editor: 'static { fn events(&self) -> Receiver; fn capabilities(&self) -> lsp_types::ClientCapabilities; fn say_hello(&self) -> Result<(), EditorError>; + + fn init(&mut self) -> Result<(), EditorError>; fn message(&mut self, msg: &str) -> Result<(), EditorError>; fn show_hover( &mut self, @@ -176,6 +178,11 @@ pub trait Editor: 'static { ) -> Result<(), EditorError>; fn show_message(&mut self, show_message_params: &ShowMessageParams) -> Result<(), EditorError>; fn show_references(&mut self, locations: &Vec) -> Result<(), EditorError>; + fn show_diagnostics( + &mut self, + text_document: &TextDocumentIdentifier, + diagnostics: &[lsp_types::Diagnostic], + ) -> Result<(), EditorError>; fn goto(&mut self, location: &Location) -> Result<(), EditorError>; fn apply_edits(&self, lines: &Vec, edits: &Vec) -> Result<(), EditorError>; fn track_all_buffers(&self) -> Result<(), EditorError>; @@ -645,6 +652,23 @@ impl Lspc { } Err(noti) => noti, }; + noti = match noti.cast::() { + Ok(params) => { + let (_handler, _tracking_file, editor) = + self.handler_for_file(¶ms.uri).ok_or_else(|| { + log::info!( + "Received changed event for nontracking file: {:?}", + params.uri + ); + MainLoopError::IgnoredMessage + })?; + let text_document = TextDocumentIdentifier::new(params.uri); + editor.show_diagnostics(&text_document, ¶ms.diagnostics)?; + + return Ok(()); + } + Err(noti) => noti, + }; log::warn!("Not supported notification: {:?}", noti); } @@ -702,6 +726,11 @@ impl Lspc { let event_receiver = self.editor.events(); let timer_tick = tick(Duration::from_millis(TIMER_TICK_MS)); + if let Err(e) = self.editor.init() { + log::error!("Editor initialization error: {:?}", e); + return; + } + loop { let selected = select(&event_receiver, &timer_tick, &self.lsp_handlers); let result = match selected { diff --git a/src/neovim.rs b/src/neovim.rs index 16e0c75..675ca0a 100644 --- a/src/neovim.rs +++ b/src/neovim.rs @@ -28,7 +28,7 @@ use rmpv::{ use serde::{ self, de::{self, SeqAccess, Visitor}, - ser::SerializeSeq, + ser::{SerializeMap, SerializeSeq}, Deserialize, Serialize, }; use url::Url; @@ -42,6 +42,7 @@ pub struct Neovim { next_id: AtomicU64, subscription_sender: Sender<(u64, Sender)>, thread: JoinHandle<()>, + buf_mapper: Arc>> } pub trait ToDisplay { @@ -443,6 +444,7 @@ impl Neovim { event_receiver, rpc_client, thread, + buf_mapper, } } @@ -493,7 +495,7 @@ impl Neovim { .map_err(|_| EditorError::Timeout) } - pub fn notify(&self, method: &str, params: &[Value]) -> Result<(), EditorError> { + pub fn notify(&self, method: &str, params: Value) -> Result<(), EditorError> { let noti = NvimMessage::RpcNotification { method: method.into(), params: Value::from(params.to_owned()), @@ -544,13 +546,14 @@ impl Neovim { .into(); self.notify( "nvim_buf_set_virtual_text", - &vec![ + vec![ buffer_id.into(), ns_id.into(), line.into(), chunks, Value::Map(Vec::new()), - ], + ] + .into(), )?; Ok(()) @@ -601,6 +604,13 @@ impl Editor for Neovim { Ok(()) } + fn init(&mut self) -> Result<(), EditorError> { + // FIXME: call `nvim_set_client_info` here. + // FIXME: call `sign_define` here. + + Ok(()) + } + fn message(&mut self, msg: &str) -> Result<(), EditorError> { self.command(&format!("echo '{}'", msg))?; Ok(()) @@ -710,6 +720,74 @@ impl Editor for Neovim { Ok(()) } + fn show_diagnostics( + &mut self, + text_document: &TextDocumentIdentifier, + diagnostics: &[lsp::Diagnostic], + ) -> Result<(), EditorError> { + struct Placement { + lnum: i64, + priority: i64, + } + + impl Serialize for Placement { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut s = serializer.serialize_map(Some(2))?; + s.serialize_entry("lnum", &self.lnum)?; + s.serialize_entry("priority", &self.priority)?; + s.end() + } + } + + let buf_id = { + let locked_buf_mapper = self.buf_mapper.lock().unwrap(); + *locked_buf_mapper.get_by_right(&text_document.uri) + .ok_or_else(|| EditorError::Failed(format!("Buffer not found for {:?}", text_document)))? + }; + + self.notify( + "nvim_call_function", + vec![ + Value::from("sign_unplace"), + vec![ + Value::from("lspc-diag"), + Value::Map(vec![(Value::from("buffer"), Value::from(buf_id))]), + ].into(), + ].into(), + )?; + + #[derive(Serialize)] + struct SignParams(i64, String, String, i64, Placement); + for diag in diagnostics { + let sign_name = match diag.severity { + Some(lsp::DiagnosticSeverity::Error) => "ALEErrorSign", + Some(lsp::DiagnosticSeverity::Warning) => "ALEWarningSign", + Some(lsp::DiagnosticSeverity::Information) + | Some(lsp::DiagnosticSeverity::Hint) => "ALEInfoSign", + None => "ALEErrorSign", + }; + let placement = Placement { + lnum: diag.range.start.line as i64, + priority: 10, + }; + let sign_params = + SignParams(0, "lspc-diag".into(), sign_name.into(), buf_id, placement); + + let params = to_value(sign_params).map_err(|e| { + EditorError::Failed(format!("Failed to encode params: {}", e.description())) + })?; + self.notify( + "nvim_call_function", + vec![Value::from("sign_place"), params].into(), + )?; + } + + Ok(()) + } + fn track_all_buffers(&self) -> Result<(), EditorError> { self.call_function("lspc#track_all_buffers", Value::Array(vec![]))?; Ok(())