diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index e6d1b18c3e..b2a7ce5b44 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -14,5 +14,7 @@ crossbeam-channel = "0.2.4" threadpool = "1.7.1" flexi_logger = "0.9.0" log = "0.4.3" +url = "1.1.0" + libeditor = { path = "../libeditor" } libanalysis = { path = "../libanalysis" } diff --git a/crates/server/src/dispatch.rs b/crates/server/src/dispatch.rs index 2da0996e3b..d9681db40d 100644 --- a/crates/server/src/dispatch.rs +++ b/crates/server/src/dispatch.rs @@ -136,6 +136,18 @@ pub fn handle_notification(not: &mut Option, f: F) -> Res } } +pub fn send_notification(io: &mut Io, params: N::Params) -> Result<()> + where + N: Notification, + N::Params: Serialize +{ + io.send(RawMsg::Notification(RawNotification { + method: N::METHOD.to_string(), + params: serde_json::to_value(params)?, + })); + Ok(()) +} + pub fn unknown_method(io: &mut Io, raw: RawRequest) -> Result<()> { error(io, raw.id, ErrorCode::MethodNotFound, "unknown method") diff --git a/crates/server/src/handlers.rs b/crates/server/src/handlers.rs index 5ee87a4ddd..1f55e8669e 100644 --- a/crates/server/src/handlers.rs +++ b/crates/server/src/handlers.rs @@ -1,6 +1,8 @@ -use languageserver_types::{Range, Position}; +use url::Url; +use languageserver_types::{Range, Position, Diagnostic, DiagnosticSeverity}; use libanalysis::World; use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; + use {req, Result, FilePath}; pub fn handle_syntax_tree( @@ -29,6 +31,23 @@ pub fn handle_extend_selection( Ok(req::ExtendSelectionResult { selections }) } +pub fn publish_diagnostics(world: World, uri: Url) -> Result { + let path = uri.file_path()?; + let file = world.file_syntax(&path)?; + let line_index = world.file_line_index(&path)?; + let diagnostics = libeditor::diagnostics(&file) + .into_iter() + .map(|d| Diagnostic { + range: to_vs_range(&line_index, d.range), + severity: Some(DiagnosticSeverity::Error), + code: None, + source: Some("libsyntax2".to_string()), + message: d.msg, + related_information: None, + }).collect(); + Ok(req::PublishDiagnosticsParams { uri, diagnostics }) +} + fn to_text_range(line_index: &LineIndex, range: Range) -> TextRange { TextRange::from_to( diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index 2d8695d230..6018350e3d 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -11,6 +11,7 @@ extern crate crossbeam_channel; extern crate threadpool; #[macro_use] extern crate log; +extern crate url; extern crate flexi_logger; extern crate libeditor; extern crate libanalysis; @@ -31,7 +32,7 @@ use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, Te use ::{ io::{Io, RawMsg}, - handlers::{handle_syntax_tree, handle_extend_selection}, + handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics}, }; pub type Result = ::std::result::Result; @@ -209,6 +210,21 @@ fn main_loop( dispatch::handle_notification::(&mut not, |params| { let path = params.text_document.file_path()?; world.change_overlay(path, Some(params.text_document.text)); + let world = world.snapshot(); + let sender = sender.clone(); + let uri = params.text_document.uri; + pool.execute(move || { + match publish_diagnostics(world, uri) { + Err(e) => { + error!("failed to compute diagnostics: {:?}", e) + } + Ok(params) => { + sender.send(Box::new(|io: &mut Io| { + dispatch::send_notification::(io, params) + })) + } + } + }); Ok(()) })?; dispatch::handle_notification::(&mut not, |mut params| { @@ -217,11 +233,30 @@ fn main_loop( .ok_or_else(|| format_err!("empty changes"))? .text; world.change_overlay(path, Some(text)); + let world = world.snapshot(); + let sender = sender.clone(); + let uri = params.text_document.uri; + pool.execute(move || { + match publish_diagnostics(world, uri) { + Err(e) => { + error!("failed to compute diagnostics: {:?}", e) + } + Ok(params) => { + sender.send(Box::new(|io: &mut Io| { + dispatch::send_notification::(io, params) + })) + } + } + }); Ok(()) })?; dispatch::handle_notification::(&mut not, |params| { let path = params.text_document.file_path()?; world.change_overlay(path, None); + dispatch::send_notification::(io, req::PublishDiagnosticsParams { + uri: params.text_document.uri, + diagnostics: Vec::new(), + })?; Ok(()) })?; @@ -252,21 +287,25 @@ trait FilePath { impl FilePath for TextDocumentItem { fn file_path(&self) -> Result { - self.uri.to_file_path() - .map_err(|()| format_err!("invalid uri: {}", self.uri)) + self.uri.file_path() } } impl FilePath for VersionedTextDocumentIdentifier { fn file_path(&self) -> Result { - self.uri.to_file_path() - .map_err(|()| format_err!("invalid uri: {}", self.uri)) + self.uri.file_path() } } impl FilePath for TextDocumentIdentifier { fn file_path(&self) -> Result { - self.uri.to_file_path() - .map_err(|()| format_err!("invalid uri: {}", self.uri)) + self.uri.file_path() + } +} + +impl FilePath for ::url::Url { + fn file_path(&self) -> Result { + self.to_file_path() + .map_err(|()| format_err!("invalid uri: {}", self)) } } diff --git a/crates/server/src/req.rs b/crates/server/src/req.rs index 4e588159bc..64e7ef4ae5 100644 --- a/crates/server/src/req.rs +++ b/crates/server/src/req.rs @@ -2,7 +2,7 @@ use languageserver_types::{TextDocumentIdentifier, Range}; pub use languageserver_types::{ request::*, notification::*, - InitializeResult, + InitializeResult, PublishDiagnosticsParams }; pub enum SyntaxTree {}