summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaid A. <47973576+Daaiid@users.noreply.github.com>2025-07-10 15:15:19 +0200
committerGitHub <noreply@github.com>2025-07-10 13:15:19 +0000
commit70710deb2b813eacc79d57436f5bd4c15c215f2e (patch)
tree2246cec18c94d3b52fd9f01e12848d9ea295431f
parent275012d7c624be85173315286752888e20996072 (diff)
Deduplicate labels for code completion (#6516)
-rw-r--r--crates/typst-ide/src/analyze.rs10
-rw-r--r--crates/typst-ide/src/complete.rs19
2 files changed, 27 insertions, 2 deletions
diff --git a/crates/typst-ide/src/analyze.rs b/crates/typst-ide/src/analyze.rs
index 76739fec..e9fb8a7d 100644
--- a/crates/typst-ide/src/analyze.rs
+++ b/crates/typst-ide/src/analyze.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use comemo::Track;
use ecow::{eco_vec, EcoString, EcoVec};
use typst::foundations::{Label, Styles, Value};
@@ -66,14 +68,22 @@ pub fn analyze_import(world: &dyn IdeWorld, source: &LinkedNode) -> Option<Value
/// - All labels and descriptions for them, if available
/// - A split offset: All labels before this offset belong to nodes, all after
/// belong to a bibliography.
+///
+/// Note: When multiple labels in the document have the same identifier,
+/// this only returns the first one.
pub fn analyze_labels(
document: &PagedDocument,
) -> (Vec<(Label, Option<EcoString>)>, usize) {
let mut output = vec![];
+ let mut seen_labels = HashSet::new();
// Labels in the document.
for elem in document.introspector.all() {
let Some(label) = elem.label() else { continue };
+ if !seen_labels.insert(label) {
+ continue;
+ }
+
let details = elem
.to_packed::<FigureElem>()
.and_then(|figure| match figure.caption.as_option() {
diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs
index bc5b3e10..0a560eb5 100644
--- a/crates/typst-ide/src/complete.rs
+++ b/crates/typst-ide/src/complete.rs
@@ -76,7 +76,7 @@ pub struct Completion {
}
/// A kind of item that can be completed.
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CompletionKind {
/// A syntactical structure.
@@ -1564,7 +1564,7 @@ mod tests {
use typst::layout::PagedDocument;
- use super::{autocomplete, Completion};
+ use super::{autocomplete, Completion, CompletionKind};
use crate::tests::{FilePos, TestWorld, WorldLike};
/// Quote a string.
@@ -1709,6 +1709,21 @@ mod tests {
.must_exclude(["bib"]);
}
+ #[test]
+ fn test_autocomplete_ref_identical_labels_returns_single_completion() {
+ let mut world = TestWorld::new("x<test> y<test>");
+ let doc = typst::compile(&world).output.ok();
+
+ let end = world.main.text().len();
+ world.main.edit(end..end, " @t");
+
+ let result = test_with_doc(&world, -1, doc.as_ref());
+ let completions = result.completions();
+ let label_count =
+ completions.iter().filter(|c| c.kind == CompletionKind::Label).count();
+ assert_eq!(label_count, 1);
+ }
+
/// Test what kind of brackets we autocomplete for function calls depending
/// on the function and existing parens.
#[test]