diff options
Diffstat (limited to 'support/texlab/src/diagnostics/latex.rs')
-rw-r--r-- | support/texlab/src/diagnostics/latex.rs | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/support/texlab/src/diagnostics/latex.rs b/support/texlab/src/diagnostics/latex.rs new file mode 100644 index 0000000000..8b15f67dfc --- /dev/null +++ b/support/texlab/src/diagnostics/latex.rs @@ -0,0 +1,105 @@ +use crate::range::RangeExt; +use crate::workspace::{Document, Uri}; +use lsp_types::*; +use once_cell::sync::Lazy; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::io::{Read, Write}; +use std::process::{Command, Stdio}; + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct LatexLintOptions { + pub on_change: Option<bool>, + pub on_save: Option<bool>, +} + +impl LatexLintOptions { + pub fn on_change(&self) -> bool { + self.on_change.unwrap_or(false) + } + + pub fn on_save(&self) -> bool { + self.on_save.unwrap_or(false) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct LatexDiagnosticsProvider { + diagnostics_by_uri: HashMap<Uri, Vec<Diagnostic>>, +} + +impl LatexDiagnosticsProvider { + pub fn get(&self, document: &Document) -> Vec<Diagnostic> { + match self.diagnostics_by_uri.get(&document.uri) { + Some(diagnostics) => diagnostics.to_owned(), + None => Vec::new(), + } + } + + pub fn update(&mut self, uri: &Uri, text: &str) { + if uri.scheme() != "file" { + return; + } + + self.diagnostics_by_uri + .insert(uri.clone(), lint(text).unwrap_or_default()); + } +} + +pub static LINE_REGEX: Lazy<Regex> = + Lazy::new(|| Regex::new("(\\d+):(\\d+):(\\d+):(\\w+):(\\w)+:(.*)").unwrap()); + +fn lint(text: &str) -> Option<Vec<Diagnostic>> { + let mut process = Command::new("chktex") + .args(&["-I0", "-f%l:%c:%d:%k:%n:%m\n"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .ok()?; + + process + .stdin + .take() + .unwrap() + .write_all(text.as_bytes()) + .ok()?; + + let mut stdout = String::new(); + process + .stdout + .take() + .unwrap() + .read_to_string(&mut stdout) + .ok()?; + + let mut diagnostics = Vec::new(); + for line in stdout.lines() { + if let Some(captures) = LINE_REGEX.captures(line) { + let line = captures[1].parse::<u64>().unwrap() - 1; + let character = captures[2].parse::<u64>().unwrap() - 1; + let digit = captures[3].parse::<u64>().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("chktex".into()), + code: Some(NumberOrString::String(code.to_owned())), + message, + severity: Some(severity), + range, + related_information: None, + }) + } + } + Some(diagnostics) +} |