summaryrefslogtreecommitdiff
path: root/support/texlab/src
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-07-04 03:03:43 +0000
committerNorbert Preining <norbert@preining.info>2022-07-04 03:03:43 +0000
commit1c10375ec46d7d83b2f1efc2a71b7ea114c889f0 (patch)
tree47b3469111105b7767111dcb89858fbc1e73377f /support/texlab/src
parent34d318af65decbdb242ae03b64bf3f53266067b6 (diff)
CTAN sync 202207040303
Diffstat (limited to 'support/texlab/src')
-rw-r--r--support/texlab/src/debouncer.rs55
-rw-r--r--support/texlab/src/diagnostics.rs177
-rw-r--r--support/texlab/src/diagnostics/bibtex.rs147
-rw-r--r--support/texlab/src/diagnostics/build.rs (renamed from support/texlab/src/diagnostics/build_log.rs)44
-rw-r--r--support/texlab/src/diagnostics/chktex.rs43
-rw-r--r--support/texlab/src/diagnostics/debouncer.rs75
-rw-r--r--support/texlab/src/diagnostics/latex.rs90
-rw-r--r--support/texlab/src/features/build.rs8
-rw-r--r--support/texlab/src/features/forward_search.rs74
-rw-r--r--support/texlab/src/lib.rs3
-rw-r--r--support/texlab/src/main.rs6
-rw-r--r--support/texlab/src/options.rs80
-rw-r--r--support/texlab/src/server.rs192
13 files changed, 575 insertions, 419 deletions
diff --git a/support/texlab/src/debouncer.rs b/support/texlab/src/debouncer.rs
new file mode 100644
index 0000000000..b123dfc433
--- /dev/null
+++ b/support/texlab/src/debouncer.rs
@@ -0,0 +1,55 @@
+use std::time::{Duration, Instant};
+
+use anyhow::Result;
+
+pub struct Sender<T> {
+ tx: crossbeam_channel::Sender<(T, crossbeam_channel::Receiver<Instant>)>,
+}
+
+impl<T> Clone for Sender<T> {
+ fn clone(&self) -> Self {
+ Self {
+ tx: self.tx.clone(),
+ }
+ }
+}
+
+impl<T> Sender<T>
+where
+ T: Send + Sync + 'static,
+{
+ pub fn send(&self, msg: T, delay: Duration) -> Result<()> {
+ self.tx.send((msg, crossbeam_channel::after(delay)))?;
+ Ok(())
+ }
+}
+
+pub struct Receiver<T> {
+ rx: crossbeam_channel::Receiver<(T, crossbeam_channel::Receiver<Instant>)>,
+}
+
+impl<T> Clone for Receiver<T> {
+ fn clone(&self) -> Self {
+ Self {
+ rx: self.rx.clone(),
+ }
+ }
+}
+
+impl<T> Receiver<T> {
+ pub fn recv(&self) -> Result<T> {
+ let (mut last_msg, delay) = self.rx.recv()?;
+ delay.recv()?;
+ while let Ok((msg, delay)) = self.rx.try_recv() {
+ delay.recv()?;
+ last_msg = msg;
+ }
+
+ Ok(last_msg)
+ }
+}
+
+pub fn unbounded<T>() -> (Sender<T>, Receiver<T>) {
+ let (tx, rx) = crossbeam_channel::unbounded();
+ (Sender { tx }, Receiver { rx })
+}
diff --git a/support/texlab/src/diagnostics.rs b/support/texlab/src/diagnostics.rs
index 5ce177b015..bdd8faf106 100644
--- a/support/texlab/src/diagnostics.rs
+++ b/support/texlab/src/diagnostics.rs
@@ -1,56 +1,167 @@
mod bibtex;
-mod build_log;
+mod build;
mod chktex;
-mod debouncer;
mod latex;
use std::sync::Arc;
-use lsp_types::{Diagnostic, Url};
-use multimap::MultiMap;
-use rustc_hash::FxHashMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, NumberOrString, Range, Url};
+use regex::Regex;
-use crate::{Options, Workspace};
-
-pub use self::debouncer::{DiagnosticsDebouncer, DiagnosticsMessage};
+use crate::Workspace;
use self::{
- bibtex::analyze_bibtex_static, build_log::analyze_build_log_static,
- chktex::analyze_latex_chktex, latex::analyze_latex_static,
+ bibtex::collect_bibtex_diagnostics, build::collect_build_diagnostics,
+ chktex::collect_chktex_diagnostics, latex::collect_latex_diagnostics,
};
-#[derive(Default)]
-pub struct DiagnosticsManager {
- static_diagnostics: FxHashMap<Arc<Url>, MultiMap<Arc<Url>, Diagnostic>>,
- chktex_diagnostics: MultiMap<Arc<Url>, Diagnostic>,
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Diagnostic {
+ pub severity: DiagnosticSeverity,
+ pub range: Range,
+ pub code: DiagnosticCode,
+ pub message: String,
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
+pub enum DiagnosticCode {
+ Latex(LatexCode),
+ Bibtex(BibtexCode),
+ Chktex(String),
+ Build(Arc<Url>),
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+pub enum LatexCode {
+ UnexpectedRCurly,
+ RCurlyInserted,
+ MismatchedEnvironment,
+}
+
+impl From<LatexCode> for String {
+ fn from(code: LatexCode) -> Self {
+ match code {
+ LatexCode::UnexpectedRCurly => "Unexpected \"}\"".to_string(),
+ LatexCode::RCurlyInserted => "Missing \"}\" inserted".to_string(),
+ LatexCode::MismatchedEnvironment => "Mismatched environment".to_string(),
+ }
+ }
}
-impl DiagnosticsManager {
- pub fn update_static(&mut self, workspace: &Workspace, uri: Arc<Url>) {
- let mut diagnostics_by_uri = MultiMap::new();
- analyze_build_log_static(workspace, &mut diagnostics_by_uri, &uri);
- analyze_bibtex_static(workspace, &mut diagnostics_by_uri, &uri);
- analyze_latex_static(workspace, &mut diagnostics_by_uri, &uri);
- self.static_diagnostics.insert(uri, diagnostics_by_uri);
+impl From<LatexCode> for NumberOrString {
+ fn from(code: LatexCode) -> Self {
+ match code {
+ LatexCode::UnexpectedRCurly => NumberOrString::Number(1),
+ LatexCode::RCurlyInserted => NumberOrString::Number(2),
+ LatexCode::MismatchedEnvironment => NumberOrString::Number(3),
+ }
}
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
+#[allow(clippy::enum_variant_names)]
+pub enum BibtexCode {
+ ExpectingLCurly,
+ ExpectingKey,
+ ExpectingRCurly,
+ ExpectingEq,
+ ExpectingFieldValue,
+}
- pub fn update_chktex(&mut self, workspace: &Workspace, uri: &Url, options: &Options) {
- analyze_latex_chktex(workspace, &mut self.chktex_diagnostics, uri, options);
+impl From<BibtexCode> for String {
+ fn from(code: BibtexCode) -> Self {
+ match code {
+ BibtexCode::ExpectingLCurly => "Expecting a curly bracket: \"{\"".to_string(),
+ BibtexCode::ExpectingKey => "Expecting a key".to_string(),
+ BibtexCode::ExpectingRCurly => "Expecting a curly bracket: \"}\"".to_string(),
+ BibtexCode::ExpectingEq => "Expecting an equality sign: \"=\"".to_string(),
+ BibtexCode::ExpectingFieldValue => "Expecting a field value".to_string(),
+ }
}
+}
- #[must_use]
- pub fn publish(&self, uri: &Url) -> Vec<Diagnostic> {
- let mut all_diagnostics = Vec::new();
- for diagnostics_by_uri in self.static_diagnostics.values() {
- if let Some(diagnostics) = diagnostics_by_uri.get_vec(uri) {
- all_diagnostics.append(&mut diagnostics.clone());
- }
+impl From<BibtexCode> for NumberOrString {
+ fn from(code: BibtexCode) -> Self {
+ match code {
+ BibtexCode::ExpectingLCurly => NumberOrString::Number(4),
+ BibtexCode::ExpectingKey => NumberOrString::Number(5),
+ BibtexCode::ExpectingRCurly => NumberOrString::Number(6),
+ BibtexCode::ExpectingEq => NumberOrString::Number(7),
+ BibtexCode::ExpectingFieldValue => NumberOrString::Number(8),
}
+ }
+}
+
+#[derive(Default, Clone)]
+pub struct DiagnosticManager {
+ all_diagnostics: Arc<DashMap<Arc<Url>, Vec<Diagnostic>>>,
+}
+
+impl DiagnosticManager {
+ pub fn push_syntax(&self, workspace: &Workspace, uri: &Url) {
+ collect_bibtex_diagnostics(&self.all_diagnostics, workspace, uri)
+ .or_else(|| collect_latex_diagnostics(&self.all_diagnostics, workspace, uri))
+ .or_else(|| collect_build_diagnostics(&self.all_diagnostics, workspace, uri));
+ }
+
+ pub fn push_chktex(&self, workspace: &Workspace, uri: &Url) {
+ collect_chktex_diagnostics(&self.all_diagnostics, workspace, uri);
+ }
- if let Some(diagnostics) = self.chktex_diagnostics.get_vec(uri) {
- all_diagnostics.append(&mut diagnostics.clone());
+ pub fn publish(&self, workspace: &Workspace, uri: &Url) -> Vec<lsp_types::Diagnostic> {
+ let options = &workspace.environment.options.diagnostics;
+
+ let mut results = Vec::new();
+ if let Some(diagnostics) = self.all_diagnostics.get(uri) {
+ for diagnostic in diagnostics.iter() {
+ if !options.allowed_patterns.is_empty()
+ && !options
+ .allowed_patterns
+ .iter()
+ .any(|pattern| pattern.0.is_match(&diagnostic.message))
+ {
+ continue;
+ }
+
+ if options
+ .ignored_patterns
+ .iter()
+ .any(|pattern| pattern.0.is_match(&diagnostic.message))
+ {
+ continue;
+ }
+
+ let source = match diagnostic.code {
+ DiagnosticCode::Latex(_) | DiagnosticCode::Bibtex(_) => "texlab",
+ DiagnosticCode::Chktex(_) => "chktex",
+ DiagnosticCode::Build(_) => "latex-build",
+ };
+
+ let code = match diagnostic.code.clone() {
+ DiagnosticCode::Latex(code) => Some(code.into()),
+ DiagnosticCode::Bibtex(code) => Some(code.into()),
+ DiagnosticCode::Chktex(code) => Some(NumberOrString::String(code)),
+ DiagnosticCode::Build(_) => None,
+ };
+
+ results.push(lsp_types::Diagnostic {
+ range: diagnostic.range,
+ code,
+ severity: Some(diagnostic.severity),
+ message: diagnostic.message.clone(),
+ source: Some(source.to_string()),
+ ..Default::default()
+ });
+ }
}
- all_diagnostics
+ results
}
}
+
+#[derive(Debug, Default)]
+pub struct DiagnosticFilter {
+ pub allowed_patterns: Vec<Regex>,
+ pub ignored_patterns: Vec<Regex>,
+}
diff --git a/support/texlab/src/diagnostics/bibtex.rs b/support/texlab/src/diagnostics/bibtex.rs
index 1beec52c5c..42e215a794 100644
--- a/support/texlab/src/diagnostics/bibtex.rs
+++ b/support/texlab/src/diagnostics/bibtex.rs
@@ -1,7 +1,7 @@
use std::sync::Arc;
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Url};
use rowan::{ast::AstNode, TextRange};
use crate::{
@@ -9,85 +9,84 @@ use crate::{
Document, LineIndexExt, Workspace,
};
-pub fn analyze_bibtex_static(
+use super::{BibtexCode, Diagnostic, DiagnosticCode};
+
+pub fn collect_bibtex_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
uri: &Url,
) -> Option<()> {
let document = workspace.documents_by_uri.get(uri)?;
let data = document.data.as_bibtex()?;
- for node in bibtex::SyntaxNode::new_root(data.green.clone()).descendants() {
- analyze_entry(document, diagnostics_by_uri, node.clone())
- .or_else(|| analyze_field(document, diagnostics_by_uri, node));
+ all_diagnostics.alter(uri, |_, mut diagnostics| {
+ diagnostics.retain(|diag| !matches!(diag.code, DiagnosticCode::Bibtex(_)));
+ diagnostics
+ });
+
+ let root = bibtex::SyntaxNode::new_root(data.green.clone());
+ for node in root.descendants() {
+ analyze_entry(all_diagnostics, document, node.clone())
+ .or_else(|| analyze_field(all_diagnostics, document, node));
}
Some(())
}
fn analyze_entry(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: bibtex::SyntaxNode,
) -> Option<()> {
let entry = bibtex::Entry::cast(node)?;
if entry.left_delim_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingLCurly;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(entry.type_token()?.text_range()),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(4)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a curly bracket: \"{\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
if entry.name_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingKey;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(entry.left_delim_token()?.text_range()),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(5)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a key".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
if entry.right_delim_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingRCurly;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(TextRange::empty(entry.syntax().text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(6)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a curly bracket: \"}\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
@@ -95,48 +94,44 @@ fn analyze_entry(
}
fn analyze_field(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: bibtex::SyntaxNode,
) -> Option<()> {
let field = bibtex::Field::cast(node)?;
if field.eq_token().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingEq;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
- .line_col_lsp_range(TextRange::empty(field.name_token()?.text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(7)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting an equality sign: \"=\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ .line_col_lsp_range(field.name_token()?.text_range()),
+
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
if field.value().is_none() {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = BibtexCode::ExpectingFieldValue;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
- .line_col_lsp_range(TextRange::empty(field.eq_token()?.text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(8)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Expecting a field value".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ .line_col_lsp_range(field.name_token()?.text_range()),
+
+ code: DiagnosticCode::Bibtex(code),
+ message: String::from(code),
+ });
+
return Some(());
}
diff --git a/support/texlab/src/diagnostics/build_log.rs b/support/texlab/src/diagnostics/build.rs
index e280d862bc..4b4ffcc8b4 100644
--- a/support/texlab/src/diagnostics/build_log.rs
+++ b/support/texlab/src/diagnostics/build.rs
@@ -1,20 +1,29 @@
use std::{path::PathBuf, sync::Arc};
-use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Position, Range, Url};
use crate::{syntax::build_log::BuildErrorLevel, Workspace};
-pub fn analyze_build_log_static(
+use super::{Diagnostic, DiagnosticCode};
+
+pub fn collect_build_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
build_log_uri: &Url,
) -> Option<()> {
let build_log_document = workspace.documents_by_uri.get(build_log_uri)?;
- let parse = build_log_document.data.as_build_log()?;
+ let build_log = build_log_document.data.as_build_log()?;
+
+ all_diagnostics.alter_all(|_, mut diagnostics| {
+ diagnostics.retain(
+ |diag| !matches!(&diag.code, DiagnosticCode::Build(uri) if uri.as_ref() == build_log_uri),
+ );
+ diagnostics
+ });
let root_document = workspace.documents_by_uri.values().find(|document| {
- if let Some(data) = document.data.as_latex() {
+ document.data.as_latex().map_or(false, |data| {
!document.uri.as_str().ends_with(".aux")
&& data
.extras
@@ -22,30 +31,22 @@ pub fn analyze_build_log_static(
.log
.iter()
.any(|u| u.as_ref() == build_log_uri)
- } else {
- false
- }
+ })
})?;
let base_path = PathBuf::from(root_document.uri.path());
-
- for error in &parse.errors {
- let pos = Position::new(error.line.unwrap_or(0), 0);
+ for error in &build_log.errors {
+ let position = Position::new(error.line.unwrap_or(0), 0);
let severity = match error.level {
BuildErrorLevel::Error => DiagnosticSeverity::ERROR,
BuildErrorLevel::Warning => DiagnosticSeverity::WARNING,
};
- let range = Range::new(pos, pos);
+ let range = Range::new(position, position);
let diagnostic = Diagnostic {
+ severity,
range,
- severity: Some(severity),
- code: None,
- code_description: None,
- source: Some("latex".into()),
+ code: DiagnosticCode::Build(Arc::clone(&build_log_document.uri)),
message: error.message.clone(),
- related_information: None,
- tags: None,
- data: None,
};
let full_path = base_path.join(&error.relative_path);
@@ -60,7 +61,8 @@ pub fn analyze_build_log_static(
Arc::clone(&root_document.uri)
};
- diagnostics_by_uri.insert(uri, diagnostic);
+ all_diagnostics.entry(uri).or_default().push(diagnostic);
}
+
Some(())
}
diff --git a/support/texlab/src/diagnostics/chktex.rs b/support/texlab/src/diagnostics/chktex.rs
index 6b6405d975..ad676bd70d 100644
--- a/support/texlab/src/diagnostics/chktex.rs
+++ b/support/texlab/src/diagnostics/chktex.rs
@@ -5,24 +5,32 @@ use std::{
sync::Arc,
};
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Range, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Range, Url};
use once_cell::sync::Lazy;
use regex::Regex;
use tempfile::tempdir;
-use crate::{Options, RangeExt, Workspace};
+use crate::{RangeExt, Workspace};
-pub fn analyze_latex_chktex(
+use super::{Diagnostic, DiagnosticCode};
+
+pub fn collect_chktex_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
uri: &Url,
- options: &Options,
) -> Option<()> {
let document = workspace.documents_by_uri.get(uri)?;
document.data.as_latex()?;
- let current_dir = options
+ all_diagnostics.alter(uri, |_, mut diagnostics| {
+ diagnostics.retain(|diag| !matches!(diag.code, DiagnosticCode::Chktex(_)));
+ diagnostics
+ });
+
+ let current_dir = workspace
+ .environment
+ .options
.root_directory
.as_ref()
.cloned()
@@ -40,15 +48,15 @@ pub fn analyze_latex_chktex(
})
.unwrap_or_else(|| ".".into());
- diagnostics_by_uri.remove(uri);
- diagnostics_by_uri.insert_many(
- Arc::clone(&document.uri),
- lint(&document.text, &current_dir).unwrap_or_default(),
- );
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .extend(lint(&document.text, &current_dir).unwrap_or_default());
+
Some(())
}
-pub static LINE_REGEX: Lazy<Regex> =
+static LINE_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new("(\\d+):(\\d+):(\\d+):(\\w+):(\\w+):(.*)").unwrap());
fn lint(text: &str, current_dir: &Path) -> io::Result<Vec<Diagnostic>> {
@@ -85,14 +93,9 @@ fn lint(text: &str, current_dir: &Path) -> io::Result<Vec<Diagnostic>> {
diagnostics.push(Diagnostic {
range,
- severity: Some(severity),
- code: Some(NumberOrString::String(code.into())),
- code_description: None,
- source: Some("chktex".into()),
+ severity,
+ code: DiagnosticCode::Chktex(code.into()),
message,
- related_information: None,
- tags: None,
- data: None,
});
}
diff --git a/support/texlab/src/diagnostics/debouncer.rs b/support/texlab/src/diagnostics/debouncer.rs
deleted file mode 100644
index 4c83636856..0000000000
--- a/support/texlab/src/diagnostics/debouncer.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use std::{
- sync::Arc,
- thread::{self, JoinHandle},
- time::{Duration, Instant},
-};
-
-use crossbeam_channel::Sender;
-use dashmap::DashMap;
-use lsp_types::Url;
-
-use crate::{Document, Workspace};
-
-pub enum DiagnosticsMessage {
- Analyze {
- workspace: Workspace,
- document: Document,
- },
- Shutdown,
-}
-
-pub struct DiagnosticsDebouncer {
- pub sender: Sender<DiagnosticsMessage>,
- handle: Option<JoinHandle<()>>,
-}
-
-impl DiagnosticsDebouncer {
- pub fn launch<A>(action: A) -> Self
- where
- A: Fn(Workspace, Document) + Send + Clone + 'static,
- {
- let (sender, receiver) = crossbeam_channel::unbounded();
-
- let handle = thread::spawn(move || {
- let pool = threadpool::Builder::new().build();
- let last_task_time_by_uri: Arc<DashMap<Arc<Url>, Instant>> = Arc::default();
- while let Ok(DiagnosticsMessage::Analyze {
- workspace,
- document,
- }) = receiver.recv()
- {
- let delay = workspace
- .environment
- .options
- .diagnostics_delay
- .unwrap_or(300);
-
- if let Some(time) = last_task_time_by_uri.get(&document.uri) {
- if time.elapsed().as_millis() < u128::from(delay) {
- continue;
- }
- }
-
- let last_task_time_by_uri = Arc::clone(&last_task_time_by_uri);
- let action = action.clone();
- pool.execute(move || {
- thread::sleep(Duration::from_millis(delay));
- last_task_time_by_uri.insert(Arc::clone(&document.uri), Instant::now());
- action(workspace, document);
- });
- }
- });
-
- Self {
- sender,
- handle: Some(handle),
- }
- }
-}
-
-impl Drop for DiagnosticsDebouncer {
- fn drop(&mut self) {
- self.sender.send(DiagnosticsMessage::Shutdown).unwrap();
- self.handle.take().unwrap().join().unwrap();
- }
-}
diff --git a/support/texlab/src/diagnostics/latex.rs b/support/texlab/src/diagnostics/latex.rs
index 2c271ad54a..a17aab3f77 100644
--- a/support/texlab/src/diagnostics/latex.rs
+++ b/support/texlab/src/diagnostics/latex.rs
@@ -1,14 +1,16 @@
use std::sync::Arc;
-use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Url};
-use multimap::MultiMap;
+use dashmap::DashMap;
+use lsp_types::{DiagnosticSeverity, Url};
use rowan::{ast::AstNode, NodeOrToken, TextRange};
use crate::{syntax::latex, Document, LineIndexExt, Workspace};
-pub fn analyze_latex_static(
+use super::{Diagnostic, DiagnosticCode, LatexCode};
+
+pub fn collect_latex_diagnostics(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
workspace: &Workspace,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
uri: &Url,
) -> Option<()> {
let document = workspace.documents_by_uri.get(uri)?;
@@ -18,25 +20,27 @@ pub fn analyze_latex_static(
let data = document.data.as_latex()?;
+ all_diagnostics.alter(uri, |_, mut diagnostics| {
+ diagnostics.retain(|diag| !matches!(diag.code, DiagnosticCode::Latex(_)));
+ diagnostics
+ });
+
for node in latex::SyntaxNode::new_root(data.green.clone()).descendants() {
- analyze_environment(document, diagnostics_by_uri, node.clone())
- .or_else(|| analyze_curly_group(document, diagnostics_by_uri, &node))
+ analyze_environment(all_diagnostics, document, node.clone())
+ .or_else(|| analyze_curly_group(all_diagnostics, document, &node))
.or_else(|| {
if node.kind() == latex::ERROR && node.first_token()?.text() == "}" {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = LatexCode::UnexpectedRCurly;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document.line_index.line_col_lsp_range(node.text_range()),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(1)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Unexpected \"}\"".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Latex(code),
+ message: String::from(code),
+ });
+
Some(())
} else {
None
@@ -48,37 +52,33 @@ pub fn analyze_latex_static(
}
fn analyze_environment(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: latex::SyntaxNode,
) -> Option<()> {
let environment = latex::Environment::cast(node)?;
let name1 = environment.begin()?.name()?.key()?;
let name2 = environment.end()?.name()?.key()?;
if name1 != name2 {
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = LatexCode::MismatchedEnvironment;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(latex::small_range(&name1)),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(3)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Mismatched environment".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Latex(code),
+ message: String::from(code),
+ });
}
Some(())
}
fn analyze_curly_group(
+ all_diagnostics: &DashMap<Arc<Url>, Vec<Diagnostic>>,
document: &Document,
- diagnostics_by_uri: &mut MultiMap<Arc<Url>, Diagnostic>,
node: &latex::SyntaxNode,
) -> Option<()> {
if !matches!(
@@ -108,22 +108,18 @@ fn analyze_curly_group(
.filter_map(NodeOrToken::into_token)
.any(|token| token.kind() == latex::R_CURLY)
{
- diagnostics_by_uri.insert(
- Arc::clone(&document.uri),
- Diagnostic {
+ let code = LatexCode::RCurlyInserted;
+ all_diagnostics
+ .entry(Arc::clone(&document.uri))
+ .or_default()
+ .push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
range: document
.line_index
.line_col_lsp_range(TextRange::empty(node.text_range().end())),
- severity: Some(DiagnosticSeverity::ERROR),
- code: Some(NumberOrString::Number(2)),
- code_description: None,
- source: Some("texlab".to_string()),
- message: "Missing \"}\" inserted".to_string(),
- related_information: None,
- tags: None,
- data: None,
- },
- );
+ code: DiagnosticCode::Latex(code),
+ message: String::from(code),
+ });
}
Some(())
diff --git a/support/texlab/src/features/build.rs b/support/texlab/src/features/build.rs
index ae465590fc..d961052061 100644
--- a/support/texlab/src/features/build.rs
+++ b/support/texlab/src/features/build.rs
@@ -164,12 +164,12 @@ impl BuildEngine {
let args: Vec<_> = options
.build
- .args()
- .into_iter()
- .map(|arg| replace_placeholder(arg, &path))
+ .args
+ .iter()
+ .map(|arg| replace_placeholder(arg.clone(), &path))
.collect();
- let mut process = Command::new(options.build.executable())
+ let mut process = Command::new(&options.build.executable)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
diff --git a/support/texlab/src/features/forward_search.rs b/support/texlab/src/features/forward_search.rs
index 521cc31a59..54629d0dc0 100644
--- a/support/texlab/src/features/forward_search.rs
+++ b/support/texlab/src/features/forward_search.rs
@@ -86,6 +86,73 @@ pub fn execute_forward_search(
Some(ForwardSearchResult { status })
}
+/// Iterate overs chunks of a string. Either returns a slice of the
+/// original string, or the placeholder replacement.
+pub struct PlaceHolderIterator<'a> {
+ remainder: &'a str,
+ tex_file: &'a str,
+ pdf_file: &'a str,
+ line_number: &'a str,
+}
+
+impl<'a> PlaceHolderIterator<'a> {
+ pub fn new(s: &'a str, tex_file: &'a str, pdf_file: &'a str, line_number: &'a str) -> Self {
+ Self {
+ remainder: s,
+ tex_file,
+ pdf_file,
+ line_number,
+ }
+ }
+
+ pub fn yield_remainder(&mut self) -> Option<&'a str> {
+ let chunk = self.remainder;
+ self.remainder = "";
+ Some(chunk)
+ }
+
+ pub fn yield_placeholder(&mut self) -> Option<&'a str> {
+ if self.remainder.len() >= 2 {
+ let placeholder = self.remainder;
+ self.remainder = &self.remainder[2..];
+ match &placeholder[1..2] {
+ "f" => Some(self.tex_file),
+ "p" => Some(self.pdf_file),
+ "l" => Some(self.line_number),
+ "%" => Some("%"), // escape %
+ _ => Some(&placeholder[0..2]),
+ }
+ } else {
+ self.remainder = &self.remainder[1..];
+ Some("%")
+ }
+ }
+
+ pub fn yield_str(&mut self, end: usize) -> Option<&'a str> {
+ let chunk = &self.remainder[..end];
+ self.remainder = &self.remainder[end..];
+ Some(chunk)
+ }
+}
+
+impl<'a> Iterator for PlaceHolderIterator<'a> {
+ type Item = &'a str;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ return if self.remainder.is_empty() {
+ None
+ } else if self.remainder.starts_with('%') {
+ self.yield_placeholder()
+ } else {
+ // yield up to the next % or to the end
+ match self.remainder.find('%') {
+ None => self.yield_remainder(),
+ Some(end) => self.yield_str(end),
+ }
+ };
+ }
+}
+
fn replace_placeholder(
tex_file: &Path,
pdf_file: &Path,
@@ -95,10 +162,9 @@ fn replace_placeholder(
let result = if argument.starts_with('"') || argument.ends_with('"') {
argument.to_string()
} else {
- argument
- .replace("%f", tex_file.to_str()?)
- .replace("%p", pdf_file.to_str()?)
- .replace("%l", &(line_number + 1).to_string())
+ let line = &(line_number + 1).to_string();
+ let it = PlaceHolderIterator::new(argument, tex_file.to_str()?, pdf_file.to_str()?, line);
+ it.collect::<Vec<&str>>().join("")
};
Some(result)
}
diff --git a/support/texlab/src/lib.rs b/support/texlab/src/lib.rs
index ce52bee1da..1f3d22d7aa 100644
--- a/support/texlab/src/lib.rs
+++ b/support/texlab/src/lib.rs
@@ -2,7 +2,8 @@ mod capabilities;
pub mod citation;
mod client;
pub mod component_db;
-pub mod diagnostics;
+mod debouncer;
+mod diagnostics;
mod dispatch;
pub mod distro;
mod document;
diff --git a/support/texlab/src/main.rs b/support/texlab/src/main.rs
index 71338f1bcd..06cbba3f5d 100644
--- a/support/texlab/src/main.rs
+++ b/support/texlab/src/main.rs
@@ -1,7 +1,7 @@
use std::{env, fs::OpenOptions, io, path::PathBuf};
use anyhow::Result;
-use clap::Parser;
+use clap::{ArgAction, Parser};
use log::LevelFilter;
use lsp_server::Connection;
use texlab::Server;
@@ -11,7 +11,7 @@ use texlab::Server;
#[clap(version)]
struct Opts {
/// Increase message verbosity (-vvvv for max verbosity)
- #[clap(short, long, parse(from_occurrences))]
+ #[clap(short, long, action = ArgAction::Count)]
verbosity: u8,
/// No output printed to stderr
@@ -19,7 +19,7 @@ struct Opts {
quiet: bool,
/// Write the logging output to FILE
- #[clap(long, name = "FILE", parse(from_os_str))]
+ #[clap(long, name = "FILE", value_parser)]
log_file: Option<PathBuf>,
/// Print version information and exit
diff --git a/support/texlab/src/options.rs b/support/texlab/src/options.rs
index fe0d81c639..241126d908 100644
--- a/support/texlab/src/options.rs
+++ b/support/texlab/src/options.rs
@@ -1,12 +1,15 @@
use std::path::PathBuf;
+use regex::Regex;
use serde::{Deserialize, Serialize};
-#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Options {
+ #[serde(default)]
pub root_directory: Option<PathBuf>,
+ #[serde(default)]
pub aux_directory: Option<PathBuf>,
#[serde(default)]
@@ -15,9 +18,14 @@ pub struct Options {
#[serde(default)]
pub latex_formatter: LatexFormatter,
+ #[serde(default)]
pub formatter_line_length: Option<i32>,
- pub diagnostics_delay: Option<u64>,
+ #[serde(default)]
+ pub diagnostics: DiagnosticsOptions,
+
+ #[serde(default = "default_diagnostics_delay")]
+ pub diagnostics_delay: u64,
#[serde(default)]
pub build: BuildOptions,
@@ -32,6 +40,10 @@ pub struct Options {
pub forward_search: ForwardSearchOptions,
}
+fn default_diagnostics_delay() -> u64 {
+ 300
+}
+
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BibtexFormatter {
@@ -61,6 +73,7 @@ impl Default for LatexFormatter {
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LatexindentOptions {
+ #[serde(default)]
pub local: Option<String>,
#[serde(default)]
@@ -70,9 +83,11 @@ pub struct LatexindentOptions {
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BuildOptions {
- pub executable: Option<String>,
+ #[serde(default = "default_build_executable")]
+ pub executable: String,
- pub args: Option<Vec<String>>,
+ #[serde(default = "default_build_args")]
+ pub args: Vec<String>,
#[serde(default)]
pub is_continuous: bool,
@@ -84,39 +99,17 @@ pub struct BuildOptions {
pub forward_search_after: bool,
}
-impl BuildOptions {
- #[must_use]
- pub fn executable(&self) -> String {
- self.executable
- .as_ref()
- .map_or_else(|| "latexmk".to_string(), Clone::clone)
- }
-
- #[must_use]
- pub fn args(&self) -> Vec<String> {
- self.args.as_ref().map_or_else(
- || {
- vec![
- "-pdf".to_string(),
- "-interaction=nonstopmode".to_string(),
- "-synctex=1".to_string(),
- "%f".to_string(),
- ]
- },
- Clone::clone,
- )
- }
+fn default_build_executable() -> String {
+ "latexmk".to_string()
}
-#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ViewerOptions {
- #[serde(default)]
- pub enabled: bool,
-
- pub executable: Option<String>,
-
- pub args: Option<String>,
+fn default_build_args() -> Vec<String> {
+ vec![
+ "-pdf".to_string(),
+ "-interaction=nonstopmode".to_string(),
+ "-synctex=1".to_string(),
+ "%f".to_string(),
+ ]
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
@@ -130,7 +123,24 @@ pub struct ChktexOptions {
}
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
pub struct ForwardSearchOptions {
+ #[serde(default)]
pub executable: Option<String>,
+
+ #[serde(default)]
pub args: Option<Vec<String>>,
}
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct DiagnosticsOptions {
+ #[serde(default)]
+ pub allowed_patterns: Vec<DiagnosticsPattern>,
+
+ #[serde(default)]
+ pub ignored_patterns: Vec<DiagnosticsPattern>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct DiagnosticsPattern(#[serde(with = "serde_regex")] pub Regex);
diff --git a/support/texlab/src/server.rs b/support/texlab/src/server.rs
index f3d2bf44f4..0d5d1f8fe1 100644
--- a/support/texlab/src/server.rs
+++ b/support/texlab/src/server.rs
@@ -1,6 +1,7 @@
use std::{
path::PathBuf,
sync::{Arc, Mutex},
+ time::Duration,
};
use anyhow::Result;
@@ -13,7 +14,8 @@ use threadpool::ThreadPool;
use crate::{
client::{send_notification, send_request},
- diagnostics::{DiagnosticsDebouncer, DiagnosticsManager, DiagnosticsMessage},
+ debouncer,
+ diagnostics::DiagnosticManager,
dispatch::{NotificationDispatcher, RequestDispatcher},
distro::Distribution,
features::{
@@ -24,14 +26,14 @@ use crate::{
ForwardSearchStatus,
},
req_queue::{IncomingData, ReqQueue},
- ClientCapabilitiesExt, DocumentLanguage, Environment, LineIndex, LineIndexExt, Options,
- Workspace, WorkspaceEvent,
+ ClientCapabilitiesExt, Document, DocumentData, DocumentLanguage, Environment, LineIndex,
+ LineIndexExt, Options, Workspace, WorkspaceEvent,
};
#[derive(Debug)]
enum InternalMessage {
SetDistro(Distribution),
- SetOptions(Options),
+ SetOptions(Arc<Options>),
}
#[derive(Clone)]
@@ -41,8 +43,8 @@ pub struct Server {
internal_rx: Receiver<InternalMessage>,
req_queue: Arc<Mutex<ReqQueue>>,
workspace: Workspace,
- static_debouncer: Arc<DiagnosticsDebouncer>,
- chktex_debouncer: Arc<DiagnosticsDebouncer>,
+ diagnostic_tx: debouncer::Sender<Workspace>,
+ diagnostic_manager: DiagnosticManager,
pool: Arc<Mutex<ThreadPool>>,
load_resolver: bool,
build_engine: Arc<BuildEngine>,
@@ -56,25 +58,17 @@ impl Server {
) -> Self {
let req_queue = Arc::default();
let workspace = Workspace::new(Environment::new(Arc::new(current_dir)));
- let diag_manager = Arc::new(Mutex::new(DiagnosticsManager::default()));
-
- let static_debouncer = Arc::new(create_static_debouncer(
- Arc::clone(&diag_manager),
- &connection,
- ));
-
- let chktex_debouncer = Arc::new(create_chktex_debouncer(diag_manager, &connection));
-
let (internal_tx, internal_rx) = crossbeam_channel::unbounded();
-
+ let diagnostic_manager = DiagnosticManager::default();
+ let diagnostic_tx = create_debouncer(connection.sender.clone(), diagnostic_manager.clone());
Self {
connection: Arc::new(connection),
internal_tx,
internal_rx,
req_queue,
workspace,
- static_debouncer,
- chktex_debouncer,
+ diagnostic_tx,
+ diagnostic_manager,
pool: Arc::new(Mutex::new(threadpool::Builder::new().build())),
load_resolver,
build_engine: Arc::default(),
@@ -173,7 +167,7 @@ impl Server {
self.spawn(move |server| {
server.register_config_capability();
server.register_file_watching();
- server.pull_config();
+ let _ = server.pull_config();
});
Ok(())
@@ -246,19 +240,17 @@ impl Server {
fn register_diagnostics_handler(&mut self) {
let (event_sender, event_receiver) = crossbeam_channel::unbounded();
- let diag_sender = self.static_debouncer.sender.clone();
+ let diagnostic_tx = self.diagnostic_tx.clone();
+ let diagnostic_manager = self.diagnostic_manager.clone();
std::thread::spawn(move || {
for event in event_receiver {
match event {
WorkspaceEvent::Changed(workspace, document) => {
- let message = DiagnosticsMessage::Analyze {
- workspace,
- document,
- };
-
- if diag_sender.send(message).is_err() {
- break;
- }
+ diagnostic_manager.push_syntax(&workspace, &document.uri);
+ let delay = workspace.environment.options.diagnostics_delay;
+ diagnostic_tx
+ .send(workspace, Duration::from_millis(delay))
+ .unwrap();
}
};
}
@@ -272,14 +264,14 @@ impl Server {
req_queue.incoming.register(id, IncomingData);
}
- fn pull_config(&self) {
+ fn pull_config(&self) -> Result<()> {
if !self
.workspace
.environment
.client_capabilities
.has_pull_configuration_support()
{
- return;
+ return Ok(());
}
let params = ConfigurationParams {
@@ -296,22 +288,38 @@ impl Server {
) {
Ok(mut json) => {
let value = json.pop().expect("invalid configuration request");
- let options = match serde_json::from_value(value) {
- Ok(new_options) => new_options,
- Err(why) => {
- warn!("Invalid configuration section \"texlab\": {}", why);
- Options::default()
- }
- };
-
+ let options = self.parse_options(value)?;
self.internal_tx
- .send(InternalMessage::SetOptions(options))
+ .send(InternalMessage::SetOptions(Arc::new(options)))
.unwrap();
}
Err(why) => {
error!("Retrieving configuration failed: {}", why);
}
};
+
+ Ok(())
+ }
+
+ fn parse_options(&self, value: serde_json::Value) -> Result<Options> {
+ let options = match serde_json::from_value(value) {
+ Ok(new_options) => new_options,
+ Err(why) => {
+ send_notification::<ShowMessage>(
+ &self.connection.sender,
+ ShowMessageParams {
+ message: format!(
+ "The texlab configuration is invalid; using the default settings instead.\nDetails: {why}"
+ ),
+ typ: MessageType::WARNING,
+ },
+ )?;
+
+ Options::default()
+ }
+ };
+
+ Ok(options)
}
fn cancel(&self, params: CancelParams) -> Result<()> {
@@ -352,18 +360,11 @@ impl Server {
.has_pull_configuration_support()
{
self.spawn(move |server| {
- server.pull_config();
+ let _ = server.pull_config();
});
} else {
- match serde_json::from_value(params.settings) {
- Ok(options) => {
- self.workspace.environment.options = Arc::new(options);
- }
- Err(why) => {
- error!("Invalid configuration: {}", why);
- }
- };
-
+ let options = self.parse_options(params.settings)?;
+ self.workspace.environment.options = Arc::new(options);
self.reparse_all()?;
}
@@ -382,12 +383,7 @@ impl Server {
self.workspace.viewport.insert(Arc::clone(&document.uri));
if self.workspace.environment.options.chktex.on_open_and_save {
- self.chktex_debouncer
- .sender
- .send(DiagnosticsMessage::Analyze {
- workspace: self.workspace.clone(),
- document,
- })?;
+ self.run_chktex(document);
}
Ok(())
@@ -421,12 +417,7 @@ impl Server {
);
if self.workspace.environment.options.chktex.on_edit {
- self.chktex_debouncer
- .sender
- .send(DiagnosticsMessage::Analyze {
- workspace: self.workspace.clone(),
- document: new_document,
- })?;
+ self.run_chktex(new_document);
};
}
None => match uri.to_file_path() {
@@ -440,7 +431,7 @@ impl Server {
Ok(())
}
- fn did_save(&self, params: DidSaveTextDocumentParams) -> Result<()> {
+ fn did_save(&mut self, params: DidSaveTextDocumentParams) -> Result<()> {
let uri = params.text_document.uri;
if let Some(request) = self
@@ -477,13 +468,9 @@ impl Server {
.filter(|_| self.workspace.environment.options.chktex.on_open_and_save)
.cloned()
{
- self.chktex_debouncer
- .sender
- .send(DiagnosticsMessage::Analyze {
- workspace: self.workspace.clone(),
- document,
- })?;
- };
+ self.run_chktex(document);
+ }
+
Ok(())
}
@@ -492,6 +479,20 @@ impl Server {
Ok(())
}
+ fn run_chktex(&mut self, document: Document) {
+ self.spawn(move |server| {
+ server
+ .diagnostic_manager
+ .push_chktex(&server.workspace, &document.uri);
+
+ let delay = server.workspace.environment.options.diagnostics_delay;
+ server
+ .diagnostic_tx
+ .send(server.workspace.clone(), Duration::from_millis(delay))
+ .unwrap();
+ });
+ }
+
fn feature_request<P>(&self, uri: Arc<Url>, params: P) -> FeatureRequest<P> {
FeatureRequest {
params,
@@ -841,7 +842,7 @@ impl Server {
self.reparse_all()?;
}
InternalMessage::SetOptions(options) => {
- self.workspace.environment.options = Arc::new(options);
+ self.workspace.environment.options = options;
self.reparse_all()?;
}
};
@@ -853,50 +854,40 @@ impl Server {
pub fn run(mut self) -> Result<()> {
self.initialize()?;
self.process_messages()?;
- drop(self.static_debouncer);
- drop(self.chktex_debouncer);
self.pool.lock().unwrap().join();
Ok(())
}
}
-fn create_static_debouncer(
- manager: Arc<Mutex<DiagnosticsManager>>,
- conn: &Connection,
-) -> DiagnosticsDebouncer {
- let sender = conn.sender.clone();
- DiagnosticsDebouncer::launch(move |workspace, document| {
- let mut manager = manager.lock().unwrap();
- manager.update_static(&workspace, Arc::clone(&document.uri));
- if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) {
- warn!("Failed to publish diagnostics: {}", why);
+fn create_debouncer(
+ lsp_sender: Sender<Message>,
+ diagnostic_manager: DiagnosticManager,
+) -> debouncer::Sender<Workspace> {
+ let (tx, rx) = debouncer::unbounded();
+ std::thread::spawn(move || {
+ while let Ok(workspace) = rx.recv() {
+ if let Err(why) = publish_diagnostics(&lsp_sender, &diagnostic_manager, &workspace) {
+ warn!("Failed to publish diagnostics: {}", why);
+ }
}
- })
-}
+ });
-fn create_chktex_debouncer(
- manager: Arc<Mutex<DiagnosticsManager>>,
- conn: &Connection,
-) -> DiagnosticsDebouncer {
- let sender = conn.sender.clone();
- DiagnosticsDebouncer::launch(move |workspace, document| {
- let mut manager = manager.lock().unwrap();
- manager.update_chktex(&workspace, &document.uri, &workspace.environment.options);
- if let Err(why) = publish_diagnostics(&sender, &workspace, &manager) {
- warn!("Failed to publish diagnostics: {}", why);
- }
- })
+ tx
}
fn publish_diagnostics(
- sender: &Sender<lsp_server::Message>,
+ lsp_sender: &Sender<lsp_server::Message>,
+ diagnostic_manager: &DiagnosticManager,
workspace: &Workspace,
- diag_manager: &DiagnosticsManager,
) -> Result<()> {
for document in workspace.documents_by_uri.values() {
- let diagnostics = diag_manager.publish(&document.uri);
+ if matches!(document.data, DocumentData::BuildLog(_)) {
+ continue;
+ }
+
+ let diagnostics = diagnostic_manager.publish(workspace, &document.uri);
send_notification::<PublishDiagnostics>(
- sender,
+ lsp_sender,
PublishDiagnosticsParams {
uri: document.uri.as_ref().clone(),
version: None,
@@ -904,6 +895,7 @@ fn publish_diagnostics(
},
)?;
}
+
Ok(())
}