summaryrefslogtreecommitdiff
path: root/crates/typst-ide/src/utils.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-11-12 15:29:26 +0100
committerLaurenz <laurmaedje@gmail.com>2024-11-13 10:21:40 +0100
commit7add9b459a3ca54fca085e71f3dd4e611941c4cc (patch)
treefe4261b5ecf601fe17de9cc948be70ad39a52d41 /crates/typst-ide/src/utils.rs
parentde59d64d103c7b2c17f6ec6d51fadbbaaab54a40 (diff)
Smarter filtering of scope completions
Diffstat (limited to 'crates/typst-ide/src/utils.rs')
-rw-r--r--crates/typst-ide/src/utils.rs66
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(())
+ }
+}