summaryrefslogtreecommitdiff
path: root/support/texlab/src/diagnostics/latex.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/diagnostics/latex.rs')
-rw-r--r--support/texlab/src/diagnostics/latex.rs105
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)
+}