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
|
use std::borrow::Cow;
use base_db::{Document, Workspace};
use line_index::LineCol;
use rowan::{TextLen, TextRange, TextSize};
use rustc_hash::FxHashMap;
use syntax::BuildError;
use url::Url;
use crate::{
types::{Diagnostic, DiagnosticData},
DiagnosticBuilder, DiagnosticSource,
};
#[derive(Debug, Default)]
struct BuildLog {
errors: FxHashMap<Url, Vec<Diagnostic>>,
}
#[derive(Debug, Default)]
pub struct BuildErrors {
logs: FxHashMap<Url, BuildLog>,
}
impl DiagnosticSource for BuildErrors {
fn update(&mut self, workspace: &Workspace, log_document: &Document) {
let mut errors: FxHashMap<Url, Vec<Diagnostic>> = FxHashMap::default();
let Some(data) = log_document.data.as_log() else {
return;
};
let parents = workspace.parents(log_document);
let Some(root_document) = parents.iter().next() else {
return;
};
let Some(base_path) = root_document.path.as_deref().and_then(|path| path.parent()) else {
return;
};
for error in &data.errors {
let full_path = base_path.join(&error.relative_path);
let Ok(full_path_uri) = Url::from_file_path(&full_path) else {
continue;
};
let tex_document = workspace.lookup(&full_path_uri).unwrap_or(root_document);
let range = find_range_of_hint(tex_document, error).unwrap_or_else(|| {
let line = error.line.unwrap_or(0);
let offset = tex_document
.line_index
.offset(LineCol { line, col: 0 })
.unwrap_or(TextSize::from(0));
TextRange::empty(offset)
});
let diagnostic = Diagnostic {
range,
data: DiagnosticData::Build(error.clone()),
};
errors
.entry(tex_document.uri.clone())
.or_default()
.push(diagnostic);
}
self.logs
.insert(log_document.uri.clone(), BuildLog { errors });
}
fn publish<'db>(
&'db mut self,
workspace: &'db Workspace,
builder: &mut DiagnosticBuilder<'db>,
) {
self.logs.retain(|uri, _| workspace.lookup(uri).is_some());
for document in workspace.iter() {
let Some(log) = self.logs.get(&document.uri) else {
continue;
};
for (uri, errors) in &log.errors {
builder.push_many(uri, errors.iter().map(Cow::Borrowed));
}
}
}
}
fn find_range_of_hint(document: &Document, error: &BuildError) -> Option<TextRange> {
let line = error.line?;
let hint = error.hint.as_deref()?;
let line_index = &document.line_index;
let line_start = line_index.offset(LineCol { line, col: 0 })?;
let line_end = line_index
.offset(LineCol {
line: line + 1,
col: 0,
})
.unwrap_or_else(|| document.text.text_len());
let line_text = &document.text[line_start.into()..line_end.into()];
let hint_start = line_start + TextSize::try_from(line_text.find(hint)?).unwrap();
let hint_end = hint_start + hint.text_len();
Some(TextRange::new(hint_start, hint_end))
}
|