diagnostics

This commit is contained in:
Aleksey Kladov 2018-08-10 23:30:11 +03:00
parent 5896cd51de
commit 36d922c87d
5 changed files with 81 additions and 9 deletions

View file

@ -14,5 +14,7 @@ crossbeam-channel = "0.2.4"
threadpool = "1.7.1" threadpool = "1.7.1"
flexi_logger = "0.9.0" flexi_logger = "0.9.0"
log = "0.4.3" log = "0.4.3"
url = "1.1.0"
libeditor = { path = "../libeditor" } libeditor = { path = "../libeditor" }
libanalysis = { path = "../libanalysis" } libanalysis = { path = "../libanalysis" }

View file

@ -136,6 +136,18 @@ pub fn handle_notification<N, F>(not: &mut Option<RawNotification>, f: F) -> Res
} }
} }
pub fn send_notification<N>(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<()> { pub fn unknown_method(io: &mut Io, raw: RawRequest) -> Result<()> {
error(io, raw.id, ErrorCode::MethodNotFound, "unknown method") error(io, raw.id, ErrorCode::MethodNotFound, "unknown method")

View file

@ -1,6 +1,8 @@
use languageserver_types::{Range, Position}; use url::Url;
use languageserver_types::{Range, Position, Diagnostic, DiagnosticSeverity};
use libanalysis::World; use libanalysis::World;
use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit}; use libeditor::{self, LineIndex, LineCol, TextRange, TextUnit};
use {req, Result, FilePath}; use {req, Result, FilePath};
pub fn handle_syntax_tree( pub fn handle_syntax_tree(
@ -29,6 +31,23 @@ pub fn handle_extend_selection(
Ok(req::ExtendSelectionResult { selections }) Ok(req::ExtendSelectionResult { selections })
} }
pub fn publish_diagnostics(world: World, uri: Url) -> Result<req::PublishDiagnosticsParams> {
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 { fn to_text_range(line_index: &LineIndex, range: Range) -> TextRange {
TextRange::from_to( TextRange::from_to(

View file

@ -11,6 +11,7 @@ extern crate crossbeam_channel;
extern crate threadpool; extern crate threadpool;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate url;
extern crate flexi_logger; extern crate flexi_logger;
extern crate libeditor; extern crate libeditor;
extern crate libanalysis; extern crate libanalysis;
@ -31,7 +32,7 @@ use languageserver_types::{TextDocumentItem, VersionedTextDocumentIdentifier, Te
use ::{ use ::{
io::{Io, RawMsg}, io::{Io, RawMsg},
handlers::{handle_syntax_tree, handle_extend_selection}, handlers::{handle_syntax_tree, handle_extend_selection, publish_diagnostics},
}; };
pub type Result<T> = ::std::result::Result<T, ::failure::Error>; pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
@ -209,6 +210,21 @@ fn main_loop(
dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| { dispatch::handle_notification::<req::DidOpenTextDocument, _>(&mut not, |params| {
let path = params.text_document.file_path()?; let path = params.text_document.file_path()?;
world.change_overlay(path, Some(params.text_document.text)); 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::<req::PublishDiagnostics>(io, params)
}))
}
}
});
Ok(()) Ok(())
})?; })?;
dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| { dispatch::handle_notification::<req::DidChangeTextDocument, _>(&mut not, |mut params| {
@ -217,11 +233,30 @@ fn main_loop(
.ok_or_else(|| format_err!("empty changes"))? .ok_or_else(|| format_err!("empty changes"))?
.text; .text;
world.change_overlay(path, Some(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::<req::PublishDiagnostics>(io, params)
}))
}
}
});
Ok(()) Ok(())
})?; })?;
dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| { dispatch::handle_notification::<req::DidCloseTextDocument, _>(&mut not, |params| {
let path = params.text_document.file_path()?; let path = params.text_document.file_path()?;
world.change_overlay(path, None); world.change_overlay(path, None);
dispatch::send_notification::<req::PublishDiagnostics>(io, req::PublishDiagnosticsParams {
uri: params.text_document.uri,
diagnostics: Vec::new(),
})?;
Ok(()) Ok(())
})?; })?;
@ -252,21 +287,25 @@ trait FilePath {
impl FilePath for TextDocumentItem { impl FilePath for TextDocumentItem {
fn file_path(&self) -> Result<PathBuf> { fn file_path(&self) -> Result<PathBuf> {
self.uri.to_file_path() self.uri.file_path()
.map_err(|()| format_err!("invalid uri: {}", self.uri))
} }
} }
impl FilePath for VersionedTextDocumentIdentifier { impl FilePath for VersionedTextDocumentIdentifier {
fn file_path(&self) -> Result<PathBuf> { fn file_path(&self) -> Result<PathBuf> {
self.uri.to_file_path() self.uri.file_path()
.map_err(|()| format_err!("invalid uri: {}", self.uri))
} }
} }
impl FilePath for TextDocumentIdentifier { impl FilePath for TextDocumentIdentifier {
fn file_path(&self) -> Result<PathBuf> { fn file_path(&self) -> Result<PathBuf> {
self.uri.to_file_path() self.uri.file_path()
.map_err(|()| format_err!("invalid uri: {}", self.uri)) }
}
impl FilePath for ::url::Url {
fn file_path(&self) -> Result<PathBuf> {
self.to_file_path()
.map_err(|()| format_err!("invalid uri: {}", self))
} }
} }

View file

@ -2,7 +2,7 @@ use languageserver_types::{TextDocumentIdentifier, Range};
pub use languageserver_types::{ pub use languageserver_types::{
request::*, notification::*, request::*, notification::*,
InitializeResult, InitializeResult, PublishDiagnosticsParams
}; };
pub enum SyntaxTree {} pub enum SyntaxTree {}