1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
use std::{
io::{BufRead, BufReader, Write},
path::PathBuf,
process::Stdio,
};
use encoding_rs_io::DecodeReaderBytesBuilder;
use lsp_types::{DiagnosticSeverity, Position, Range};
use once_cell::sync::Lazy;
use regex::Regex;
use crate::{
db::{
diagnostics::{Diagnostic, DiagnosticCode},
Document, Workspace,
},
Db,
};
#[derive(Debug)]
pub struct Command {
text: String,
working_dir: PathBuf,
}
impl Command {
pub fn new(db: &dyn Db, document: Document) -> Option<Self> {
document.parse(db).as_tex()?;
let workspace = Workspace::get(db);
let parent = workspace
.parents(db, document)
.iter()
.next()
.map_or(document, Clone::clone);
let working_dir = workspace
.working_dir(db, parent.directory(db))
.path(db)
.as_deref()?
.to_owned();
log::debug!("Calling ChkTeX from directory: {}", working_dir.display());
let text = document.text(db).clone();
Some(Self { text, working_dir })
}
pub fn run(self) -> std::io::Result<Vec<Diagnostic>> {
let mut child = std::process::Command::new("chktex")
.args(["-I0", "-f%l:%c:%d:%k:%n:%m\n"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.current_dir(self.working_dir)
.spawn()?;
let stdout = child.stdout.take().unwrap();
let reader = std::thread::spawn(move || {
let mut diagnostics = Vec::new();
let reader = BufReader::new(
DecodeReaderBytesBuilder::new()
.encoding(Some(encoding_rs::UTF_8))
.utf8_passthru(true)
.strip_bom(true)
.build(stdout),
);
for line in reader.lines().flatten() {
let captures = LINE_REGEX.captures(&line).unwrap();
let line = captures[1].parse::<u32>().unwrap() - 1;
let character = captures[2].parse::<u32>().unwrap() - 1;
let digit = captures[3].parse::<u32>().unwrap();
let kind = &captures[4];
let code = &captures[5];
let message = captures[6].into();
let range = Range::new(
Position::new(line, character),
Position::new(line, character + digit),
);
let severity = match kind {
"Message" => DiagnosticSeverity::INFORMATION,
"Warning" => DiagnosticSeverity::WARNING,
_ => DiagnosticSeverity::ERROR,
};
diagnostics.push(Diagnostic {
range,
severity,
code: DiagnosticCode::Chktex(code.into()),
message,
});
}
diagnostics
});
let mut stdin = child.stdin.take().unwrap();
let bytes = self.text.into_bytes();
let writer = std::thread::spawn(move || stdin.write_all(&bytes));
child.wait()?;
writer.join().unwrap()?;
let diagnostics = reader.join().unwrap();
Ok(diagnostics)
}
}
static LINE_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new("(\\d+):(\\d+):(\\d+):(\\w+):(\\w+):(.*)").unwrap());
|