diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-11-12 15:29:26 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2024-11-13 10:21:40 +0100 |
| commit | 7add9b459a3ca54fca085e71f3dd4e611941c4cc (patch) | |
| tree | fe4261b5ecf601fe17de9cc948be70ad39a52d41 /crates/typst-ide/src/utils.rs | |
| parent | de59d64d103c7b2c17f6ec6d51fadbbaaab54a40 (diff) | |
Smarter filtering of scope completions
Diffstat (limited to 'crates/typst-ide/src/utils.rs')
| -rw-r--r-- | crates/typst-ide/src/utils.rs | 66 |
1 files changed, 65 insertions, 1 deletions
diff --git a/crates/typst-ide/src/utils.rs b/crates/typst-ide/src/utils.rs index ad8ed6b5..cd66ec8f 100644 --- a/crates/typst-ide/src/utils.rs +++ b/crates/typst-ide/src/utils.rs @@ -1,9 +1,10 @@ use std::fmt::Write; +use std::ops::ControlFlow; use comemo::Track; use ecow::{eco_format, EcoString}; use typst::engine::{Engine, Route, Sink, Traced}; -use typst::foundations::Scope; +use typst::foundations::{Scope, Value}; use typst::introspection::Introspector; use typst::syntax::{LinkedNode, SyntaxKind}; use typst::text::{FontInfo, FontStyle}; @@ -125,3 +126,66 @@ pub fn globals<'a>(world: &'a dyn IdeWorld, leaf: &LinkedNode) -> &'a Scope { library.global.scope() } } + +/// Checks whether the given value or any of its constituent parts satisfy the +/// predicate. +pub fn check_value_recursively( + value: &Value, + predicate: impl Fn(&Value) -> bool, +) -> bool { + let mut searcher = Searcher { steps: 0, predicate, max_steps: 1000 }; + match searcher.find(value) { + ControlFlow::Break(matching) => matching, + ControlFlow::Continue(_) => false, + } +} + +/// Recursively searches for a value that passes the filter, but without +/// exceeding a maximum number of search steps. +struct Searcher<F> { + max_steps: usize, + steps: usize, + predicate: F, +} + +impl<F> Searcher<F> +where + F: Fn(&Value) -> bool, +{ + fn find(&mut self, value: &Value) -> ControlFlow<bool> { + if (self.predicate)(value) { + return ControlFlow::Break(true); + } + + if self.steps > self.max_steps { + return ControlFlow::Break(false); + } + + self.steps += 1; + + match value { + Value::Dict(dict) => { + self.find_iter(dict.iter().map(|(_, v)| v))?; + } + Value::Content(content) => { + self.find_iter(content.fields().iter().map(|(_, v)| v))?; + } + Value::Module(module) => { + self.find_iter(module.scope().iter().map(|(_, v, _)| v))?; + } + _ => {} + } + + ControlFlow::Continue(()) + } + + fn find_iter<'a>( + &mut self, + iter: impl Iterator<Item = &'a Value>, + ) -> ControlFlow<bool> { + for item in iter { + self.find(item)?; + } + ControlFlow::Continue(()) + } +} |
