From 2319b116c8cc526f31915124bb1b897d7f6654d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20F=C3=B6rster?= Date: Sun, 26 May 2019 11:00:04 +0200 Subject: [PATCH] Run ChkTeX after saving a document --- src/diagnostics/build.rs | 2 - src/diagnostics/latex.rs | 88 ++++++++++++++++++++++++++++++++++++++++ src/diagnostics/mod.rs | 9 +++- src/event.rs | 1 + src/server.rs | 16 ++++++-- 5 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 src/diagnostics/latex.rs diff --git a/src/diagnostics/build.rs b/src/diagnostics/build.rs index c55d81012..1bc7bec61 100644 --- a/src/diagnostics/build.rs +++ b/src/diagnostics/build.rs @@ -1,9 +1,7 @@ use crate::build::log_parser::parse_build_log; -use crate::feature::FeatureRequest; use crate::workspace::Document; use lsp_types::{Diagnostic, Uri}; use std::collections::HashMap; -use std::sync::Arc; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct BuildDiagnosticsProvider { diff --git a/src/diagnostics/latex.rs b/src/diagnostics/latex.rs new file mode 100644 index 000000000..00a93b15c --- /dev/null +++ b/src/diagnostics/latex.rs @@ -0,0 +1,88 @@ +use crate::workspace::Document; +use lazy_static::lazy_static; +use lsp_types::*; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; +use std::collections::HashMap; +use std::fs::File; +use std::path::Path; +use std::process::Command; + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct LatexLinterConfig { + pub on_save: bool, +} + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct LatexDiagnosticsProvider { + diagnostics_by_uri: HashMap>, +} + +impl LatexDiagnosticsProvider { + pub fn get(&self, document: &Document) -> Vec { + match self.diagnostics_by_uri.get(&document.uri) { + Some(diagnostics) => diagnostics.to_owned(), + None => Vec::new(), + } + } + + pub fn update(&mut self, uri: &Uri) { + if uri.scheme() != "file" { + return; + } + + let path = uri.to_file_path().unwrap(); + self.diagnostics_by_uri + .insert(uri.clone(), lint(&path).unwrap_or_default()); + } +} + +const LINE_PATTERN: &str = "(\\d+):(\\d+):(\\d+):(\\w+):(\\w)+:(.*)"; + +lazy_static! { + static ref LINE_REGEX: Regex = Regex::new(LINE_PATTERN).unwrap(); +} + +fn lint(path: &Path) -> Option> { + let file = File::open(path).ok()?; + let output = Command::new("chktex") + .args(&["-I0", "-f%l:%c:%d:%k:%n:%m\n"]) + .stdin(file) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + let mut diagnostics = Vec::new(); + let stdout = String::from_utf8(output.stdout).ok()?; + for line in stdout.lines() { + if let Some(captures) = LINE_REGEX.captures(line) { + let line = captures[1].parse::().unwrap() - 1; + let character = captures[2].parse::().unwrap() - 1; + let digit = captures[3].parse::().unwrap(); + let kind = &captures[4]; + let code = &captures[5]; + let message = captures[6].to_owned(); + let range = Range::new_simple(line, character, line, character + digit); + let severity = match kind { + "Message" => DiagnosticSeverity::Information, + "Warning" => DiagnosticSeverity::Warning, + _ => DiagnosticSeverity::Error, + }; + + diagnostics.push(Diagnostic { + source: Some(Cow::from("chktex")), + code: Some(NumberOrString::String(code.to_owned())), + message: Cow::from(message), + severity: Some(severity), + range, + related_information: None, + }) + } + } + Some(diagnostics) +} diff --git a/src/diagnostics/mod.rs b/src/diagnostics/mod.rs index 71eacecbb..c86799403 100644 --- a/src/diagnostics/mod.rs +++ b/src/diagnostics/mod.rs @@ -1,14 +1,18 @@ mod bibtex; mod build; +mod latex; -use crate::diagnostics::bibtex::BibtexDiagnosticsProvider; -use crate::diagnostics::build::BuildDiagnosticsProvider; +use self::bibtex::BibtexDiagnosticsProvider; +use self::build::BuildDiagnosticsProvider; +use self::latex::LatexDiagnosticsProvider; +pub use self::latex::LatexLinterConfig; use crate::workspace::Document; use lsp_types::Diagnostic; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct DiagnosticsManager { pub build: BuildDiagnosticsProvider, + pub latex: LatexDiagnosticsProvider, pub bibtex: BibtexDiagnosticsProvider, } @@ -16,6 +20,7 @@ impl DiagnosticsManager { pub fn get(&self, document: &Document) -> Vec { let mut diagnostics = Vec::new(); diagnostics.append(&mut self.build.get(document)); + diagnostics.append(&mut self.latex.get(document)); diagnostics.append(&mut self.bibtex.get(document)); diagnostics } diff --git a/src/event.rs b/src/event.rs index a8f628230..d66833225 100644 --- a/src/event.rs +++ b/src/event.rs @@ -7,6 +7,7 @@ pub enum Event { Initialized, WorkspaceChanged, LogChanged { tex_uri: Uri, log_path: PathBuf }, + Saved(Uri), } #[derive(Debug, Default)] diff --git a/src/server.rs b/src/server.rs index 5583fd258..87457f8e3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -2,7 +2,7 @@ use crate::client::LspClient; use crate::completion::CompletionProvider; use crate::data::completion::LatexComponentDatabase; use crate::definition::DefinitionProvider; -use crate::diagnostics::DiagnosticsManager; +use crate::diagnostics::{DiagnosticsManager, LatexLinterConfig}; use crate::event::{Event, EventManager}; use crate::feature::FeatureRequest; use crate::folding::FoldingProvider; @@ -74,7 +74,9 @@ impl LatexLspServer { change: Some(TextDocumentSyncKind::Full), will_save: None, will_save_wait_until: None, - save: None, + save: Some(SaveOptions { + include_text: Some(false), + }), }, )), hover_provider: Some(true), @@ -158,7 +160,8 @@ impl LatexLspServer { } #[jsonrpc_method("textDocument/didSave", kind = "notification")] - pub fn did_save(&self, _params: DidSaveTextDocumentParams) { + pub fn did_save(&self, params: DidSaveTextDocumentParams) { + self.event_manager.push(Event::Saved(params.text_document.uri)); self.event_manager.push(Event::WorkspaceChanged); } @@ -365,6 +368,13 @@ impl jsonrpc::EventHandler for LatexLspServer { })); } } + Event::Saved(uri) => { + let config: LatexLinterConfig = await!(self.configuration("latex.lint")); + if config.on_save { + let mut diagnostics_manager = await!(self.diagnostics_manager.lock()); + diagnostics_manager.latex.update(&uri); + } + } Event::LogChanged { tex_uri, log_path } => { if let Ok(log) = std::fs::read_to_string(&log_path) { let mut diagnostics_manager = await!(self.diagnostics_manager.lock());