From 57e209f7e09c2470ca77788caa008b7e7daf676c Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:26:01 +0800 Subject: [PATCH] docs: split and documenting document highlight worker (#989) --- crates/tinymist-query/src/analysis.rs | 2 + .../src/analysis/doc_highlight.rs | 126 +++++++++++++++++ .../tinymist-query/src/document_highlight.rs | 127 +----------------- 3 files changed, 133 insertions(+), 122 deletions(-) create mode 100644 crates/tinymist-query/src/analysis/doc_highlight.rs diff --git a/crates/tinymist-query/src/analysis.rs b/crates/tinymist-query/src/analysis.rs index 9c0379f8..6261e6d5 100644 --- a/crates/tinymist-query/src/analysis.rs +++ b/crates/tinymist-query/src/analysis.rs @@ -10,6 +10,8 @@ pub mod code_action; pub use code_action::*; pub mod color_expr; pub use color_expr::*; +pub mod doc_highlight; +pub use doc_highlight::*; pub mod link_exprs; pub use link_exprs::*; pub mod stats; diff --git a/crates/tinymist-query/src/analysis/doc_highlight.rs b/crates/tinymist-query/src/analysis/doc_highlight.rs new file mode 100644 index 00000000..5c657bda --- /dev/null +++ b/crates/tinymist-query/src/analysis/doc_highlight.rs @@ -0,0 +1,126 @@ +//! Analyze related expressions to highlight in a source file. + +use crate::{prelude::*, syntax::node_ancestors}; + +/// Analyzes the document and provides related expression information to +/// highlight. +pub struct DocumentHighlightWorker<'a> { + /// The local analysis context to work with. + ctx: &'a mut LocalContext, + /// The source document to analyze. + source: &'a Source, + /// The related expressions to provide. + pub annotated: Vec, + /// The worklist to check the nodes. + worklist: Vec>, +} + +impl<'a> DocumentHighlightWorker<'a> { + /// Creates a new worker + pub fn new(ctx: &'a mut LocalContext, source: &'a Source) -> Self { + Self { + ctx, + source, + annotated: Vec::new(), + worklist: Vec::new(), + } + } + + /// Starts to work + pub fn work(&mut self, mut node: &'a LinkedNode<'a>) -> Option<()> { + loop { + match node.kind() { + SyntaxKind::For + | SyntaxKind::While + | SyntaxKind::Break + | SyntaxKind::Continue + | SyntaxKind::LoopBreak + | SyntaxKind::LoopContinue => return self.work_loop(node), + SyntaxKind::Arrow + | SyntaxKind::Params + | SyntaxKind::Return + | SyntaxKind::FuncReturn => return self.work_func(node), + _ => {} + } + node = node.parent()?; + } + } + + fn work_loop(&mut self, node: &'a LinkedNode<'a>) -> Option<()> { + let _ = self.ctx; + + // find the nearest loop node + let loop_node = node_ancestors(node) + .find(|node| matches!(node.kind(), SyntaxKind::ForLoop | SyntaxKind::WhileLoop))?; + + // find the first key word of the loop node + let keyword = loop_node.children().find(|node| node.kind().is_keyword()); + if let Some(keyword) = keyword { + self.annotate(&keyword); + } + + self.check_children(loop_node); + self.check(Self::check_loop); + + crate::log_debug_ct!("highlights: {:?}", self.annotated); + Some(()) + } + + fn work_func(&mut self, _node: &'a LinkedNode<'a>) -> Option<()> { + None + } + + /// Annotate the node for highlight + fn annotate(&mut self, node: &LinkedNode) { + let mut rng = node.range(); + + // if previous node is hash + if rng.start > 0 && self.source.text().as_bytes()[rng.start - 1] == b'#' { + rng.start -= 1; + } + + self.annotated.push(DocumentHighlight { + range: self.ctx.to_lsp_range(rng, self.source), + kind: None, + }); + } + + /// Consumes the worklist and checks the nodes + fn check(&mut self, check: F) + where + F: Fn(&mut Self, LinkedNode<'a>), + { + while let Some(node) = self.worklist.pop() { + check(self, node); + } + } + + /// Pushes the children of the node to check + fn check_children(&mut self, node: &LinkedNode<'a>) { + if node.get().children().len() == 0 { + return; + } + + for child in node.children() { + self.worklist.push(child.clone()); + } + } + + fn check_loop(&mut self, node: LinkedNode<'a>) { + match node.kind() { + SyntaxKind::ForLoop + | SyntaxKind::WhileLoop + | SyntaxKind::Closure + | SyntaxKind::Contextual => { + return; + } + SyntaxKind::LoopBreak | SyntaxKind::LoopContinue => { + self.annotate(&node); + return; + } + _ => {} + } + + self.check_children(&node); + } +} diff --git a/crates/tinymist-query/src/document_highlight.rs b/crates/tinymist-query/src/document_highlight.rs index af1c4ce7..f1d32ae9 100644 --- a/crates/tinymist-query/src/document_highlight.rs +++ b/crates/tinymist-query/src/document_highlight.rs @@ -1,6 +1,6 @@ use typst_shim::syntax::LinkedNodeExt; -use crate::{prelude::*, syntax::node_ancestors, SemanticRequest}; +use crate::{analysis::doc_highlight::DocumentHighlightWorker, prelude::*, SemanticRequest}; /// The [`textDocument/documentHighlight`] request /// @@ -21,131 +21,14 @@ impl SemanticRequest for DocumentHighlightRequest { let cursor = ctx.to_typst_pos(self.position, &source)?; let root = LinkedNode::new(source.root()); - let mut node = &root.leaf_at_compat(cursor)?; + let node = root.leaf_at_compat(cursor)?; - loop { - match node.kind() { - SyntaxKind::For - | SyntaxKind::While - | SyntaxKind::Break - | SyntaxKind::Continue - | SyntaxKind::LoopBreak - | SyntaxKind::LoopContinue => { - return DocumentHighlightWorker::new(ctx, &source).highlight_loop_of(node) - } - SyntaxKind::Arrow - | SyntaxKind::Params - | SyntaxKind::Return - | SyntaxKind::FuncReturn => return highlight_func_returns(ctx, node), - _ => {} - } - node = node.parent()?; - } + let mut worker = DocumentHighlightWorker::new(ctx, &source); + worker.work(&node)?; + (!worker.annotated.is_empty()).then_some(worker.annotated) } } -struct DocumentHighlightWorker<'a> { - ctx: &'a mut LocalContext, - current: &'a Source, - highlights: Vec, - worklist: Vec>, -} - -impl<'a> DocumentHighlightWorker<'a> { - fn new(ctx: &'a mut LocalContext, current: &'a Source) -> Self { - Self { - ctx, - current, - highlights: Vec::new(), - worklist: Vec::new(), - } - } - - fn finish(self) -> Option> { - (!self.highlights.is_empty()).then_some(self.highlights) - } - - fn annotate(&mut self, node: &LinkedNode) { - let mut rng = node.range(); - - // if previous node is hash - if rng.start > 0 && self.current.text().as_bytes()[rng.start - 1] == b'#' { - rng.start -= 1; - } - - self.highlights.push(DocumentHighlight { - range: self.ctx.to_lsp_range(rng, self.current), - kind: None, - }); - } - - fn check(&mut self, check: F) - where - F: Fn(&mut Self, LinkedNode<'a>), - { - while let Some(node) = self.worklist.pop() { - check(self, node); - } - } - - fn check_children(&mut self, node: &LinkedNode<'a>) { - if node.get().children().len() == 0 { - return; - } - - for child in node.children() { - self.worklist.push(child.clone()); - } - } - - fn check_loop(&mut self, node: LinkedNode<'a>) { - match node.kind() { - SyntaxKind::ForLoop - | SyntaxKind::WhileLoop - | SyntaxKind::Closure - | SyntaxKind::Contextual => { - return; - } - SyntaxKind::LoopBreak | SyntaxKind::LoopContinue => { - self.annotate(&node); - return; - } - _ => {} - } - - self.check_children(&node); - } - - fn highlight_loop_of(mut self, node: &'a LinkedNode<'a>) -> Option> { - let _ = self.ctx; - - // find the nearest loop node - let loop_node = node_ancestors(node) - .find(|node| matches!(node.kind(), SyntaxKind::ForLoop | SyntaxKind::WhileLoop))?; - - // find the first key word of the loop node - let keyword = loop_node.children().find(|node| node.kind().is_keyword()); - if let Some(keyword) = keyword { - self.annotate(&keyword); - } - - self.check_children(loop_node); - self.check(Self::check_loop); - - crate::log_debug_ct!("highlights: {:?}", self.highlights); - self.finish() - } -} - -fn highlight_func_returns( - ctx: &mut LocalContext, - node: &LinkedNode, -) -> Option> { - let _ = ctx; - let _ = node; - None -} - #[cfg(test)] mod tests { use super::*;