summaryrefslogtreecommitdiff
path: root/support/texlab/src/db/diagnostics/tex.rs
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-12-30 03:01:26 +0000
committerNorbert Preining <norbert@preining.info>2022-12-30 03:01:26 +0000
commit844019377157163b461e0fd4a66592e61963a530 (patch)
tree32f61991c6a5acc3a3359ffc2cdefdd2aa004268 /support/texlab/src/db/diagnostics/tex.rs
parent55c69feeef908f49007708be194b7bb1c607f302 (diff)
CTAN sync 202212300301
Diffstat (limited to 'support/texlab/src/db/diagnostics/tex.rs')
-rw-r--r--support/texlab/src/db/diagnostics/tex.rs125
1 files changed, 125 insertions, 0 deletions
diff --git a/support/texlab/src/db/diagnostics/tex.rs b/support/texlab/src/db/diagnostics/tex.rs
new file mode 100644
index 0000000000..50233f8bd6
--- /dev/null
+++ b/support/texlab/src/db/diagnostics/tex.rs
@@ -0,0 +1,125 @@
+use lsp_types::DiagnosticSeverity;
+use rowan::{ast::AstNode, NodeOrToken, TextRange};
+
+use crate::{db::document::Document, syntax::latex, util::line_index_ext::LineIndexExt, Db};
+
+use super::{Diagnostic, DiagnosticCode, TexCode};
+
+#[salsa::tracked(return_ref)]
+pub fn collect(db: &dyn Db, document: Document) -> Vec<Diagnostic> {
+ let mut results = Vec::new();
+
+ if !document.location(db).uri(db).as_str().ends_with(".tex") {
+ return results;
+ }
+
+ let data = match document.parse(db).as_tex() {
+ Some(data) => data,
+ None => return results,
+ };
+
+ for node in data.root(db).descendants() {
+ analyze_environment(db, document, node.clone(), &mut results)
+ .or_else(|| analyze_curly_group(db, document, node.clone(), &mut results))
+ .or_else(|| analyze_curly_braces(document, db, node, &mut results));
+ }
+
+ results
+}
+
+fn analyze_environment(
+ db: &dyn Db,
+ document: Document,
+ node: latex::SyntaxNode,
+ results: &mut Vec<Diagnostic>,
+) -> Option<()> {
+ let environment = latex::Environment::cast(node)?;
+ let name1 = environment.begin()?.name()?.key()?;
+ let name2 = environment.end()?.name()?.key()?;
+ if name1 != name2 {
+ let code = TexCode::MismatchedEnvironment;
+ results.push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
+ range: document
+ .contents(db)
+ .line_index(db)
+ .line_col_lsp_range(latex::small_range(&name1)),
+ code: DiagnosticCode::Tex(code),
+ message: String::from(code),
+ });
+ }
+
+ Some(())
+}
+
+fn analyze_curly_group(
+ db: &dyn Db,
+ document: Document,
+ node: latex::SyntaxNode,
+ results: &mut Vec<Diagnostic>,
+) -> Option<()> {
+ if !matches!(
+ node.kind(),
+ latex::CURLY_GROUP
+ | latex::CURLY_GROUP_COMMAND
+ | latex::CURLY_GROUP_KEY_VALUE
+ | latex::CURLY_GROUP_WORD
+ | latex::CURLY_GROUP_WORD_LIST
+ ) {
+ return None;
+ }
+
+ let is_inside_verbatim_environment = node
+ .ancestors()
+ .filter_map(latex::Environment::cast)
+ .filter_map(|env| env.begin())
+ .filter_map(|begin| begin.name())
+ .filter_map(|name| name.key())
+ .any(|name| {
+ ["asy", "lstlisting", "minted", "verbatim"].contains(&name.to_string().as_str())
+ });
+
+ if !is_inside_verbatim_environment
+ && !node
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .any(|token| token.kind() == latex::R_CURLY)
+ {
+ let code = TexCode::RCurlyInserted;
+ results.push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
+ range: document
+ .contents(db)
+ .line_index(db)
+ .line_col_lsp_range(TextRange::empty(node.text_range().end())),
+ code: DiagnosticCode::Tex(code),
+ message: String::from(code),
+ });
+ }
+
+ Some(())
+}
+
+fn analyze_curly_braces(
+ document: Document,
+ db: &dyn Db,
+ node: rowan::SyntaxNode<latex::LatexLanguage>,
+ results: &mut Vec<Diagnostic>,
+) -> Option<()> {
+ if node.kind() == latex::ERROR && node.first_token()?.text() == "}" {
+ let code = TexCode::UnexpectedRCurly;
+ results.push(Diagnostic {
+ severity: DiagnosticSeverity::ERROR,
+ range: document
+ .contents(db)
+ .line_index(db)
+ .line_col_lsp_range(node.text_range()),
+ code: DiagnosticCode::Tex(code),
+ message: String::from(code),
+ });
+
+ Some(())
+ } else {
+ None
+ }
+}