badness_parser/semantic/label.rs
1//! Label definitions and reference uses — the data of the per-file
2//! label/reference model: `Vec`-stored records addressed by newtype ids.
3
4use rowan::TextRange;
5use smol_str::SmolStr;
6
7/// Index of a [`LabelDef`] in [`SemanticModel::labels`](super::SemanticModel).
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct LabelId(pub(crate) u32);
10
11impl LabelId {
12 pub(crate) fn from_index(i: usize) -> Self {
13 Self(i as u32)
14 }
15}
16
17/// Index of a [`LabelRef`] in [`SemanticModel::refs`](super::SemanticModel).
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct RefId(pub(crate) u32);
20
21impl RefId {
22 pub(crate) fn from_index(i: usize) -> Self {
23 Self(i as u32)
24 }
25}
26
27/// A label definition site: either `\label{key}` or a curated environment's
28/// `label=key` option.
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct LabelDef {
31 pub name: SmolStr,
32 /// Range of the definition for diagnostics and go-to-definition. For
33 /// `\label`, this spans the control word through its key group and excludes
34 /// any *second* group the greedy parser may have over-attached (`\label`'s
35 /// arity is unknown at parse time; see `builder::label_range`). For an
36 /// environment option, it spans the complete `label=value` entry.
37 pub range: TextRange,
38 /// Range of just the trimmed key text (`sec:intro` in either
39 /// `\label{sec:intro}` or `label={sec:intro}`). This is the precise span a
40 /// rename rewrites—narrower than [`range`](Self::range), which spans the
41 /// whole definition.
42 pub key_range: TextRange,
43 /// Set by the resolve pass when any reference in this file uses `name`.
44 /// This field is per-file only; project consumers consult the cross-file
45 /// resolver for namespace-wide references.
46 pub referenced: bool,
47}
48
49/// Which reference-family command produced a [`LabelRef`]. A small explicit
50/// table (the analog of `project::IncludeKind`) kept distinct so later passes
51/// can honor differences (`\cref` capitalization, `\nameref` text, …).
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub enum RefCommand {
54 // Single-key (LaTeX / amsmath / hyperref).
55 Ref,
56 PageRef,
57 EqRef,
58 AutoRef,
59 NameRef,
60 // Comma-separated key list (cleveref / varioref).
61 Cref,
62 CrefUpper,
63 Vref,
64 VrefUpper,
65 CpageRef,
66}
67
68impl RefCommand {
69 /// Whether this command accepts a comma-separated key list (cleveref /
70 /// varioref) rather than a single key.
71 pub fn is_key_list(self) -> bool {
72 matches!(
73 self,
74 RefCommand::Cref
75 | RefCommand::CrefUpper
76 | RefCommand::Vref
77 | RefCommand::VrefUpper
78 | RefCommand::CpageRef
79 )
80 }
81}
82
83/// A reference *use* site — one per key. A `\cref{a,b,c}` produces three
84/// `LabelRef`s, each carrying the same command kind and command range.
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct LabelRef {
87 pub name: SmolStr,
88 pub command: RefCommand,
89 /// Range of the enclosing command, shared by all keys split from one
90 /// `\cref{a,b,c}` — used for go-to-def / find-references navigation, which
91 /// jumps to the whole command.
92 pub range: TextRange,
93 /// Range of just this key inside the braces (`b` in `\cref{a,b}`), trimmed of
94 /// surrounding whitespace. Unlike [`range`](Self::range), this *is* per-key, so
95 /// a rename rewrites exactly one key of a list command.
96 pub key_range: TextRange,
97 /// Set by the resolve pass when `name` matches a `\label` in *this* file.
98 pub resolved: bool,
99}
100
101/// Which definer command produced a [`GlossaryDef`]. Kept distinct so a later
102/// hover/goto-def pass can render the entry appropriately (an acronym has
103/// short/long groups; an entry has a key=value settings group).
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub enum GlossaryDefKind {
106 /// `\newglossaryentry` / `\longnewglossaryentry`.
107 Entry,
108 /// `\newacronym` (glossaries).
109 Acronym,
110 /// `\newabbreviation` (glossaries-extra).
111 Abbreviation,
112}
113
114/// A glossary/acronym key definition site (`\newglossaryentry{key}{…}`,
115/// `\newacronym[opts]{key}{short}{long}`, …). The definition-side analog of
116/// [`LabelDef`]; the reference side (`\gls{key}`) is classified directly off the
117/// CST by completion and carries no stored model record yet.
118#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct GlossaryDef {
120 /// The entry key, as authored.
121 pub key: SmolStr,
122 pub kind: GlossaryDefKind,
123 /// Range of the command through its key group (like [`LabelDef::range`]) —
124 /// the navigation target for a future go-to-definition.
125 pub range: TextRange,
126 /// Range of just the trimmed key text inside the braces — the precise span a
127 /// future rename would rewrite.
128 pub key_range: TextRange,
129}
130
131/// Which definer command produced a [`ColorDef`]. Kept distinct so a later
132/// hover/goto-def pass can render the definition appropriately (`\definecolor`
133/// carries a model + spec; `\colorlet` aliases an existing color).
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
135pub enum ColorDefKind {
136 /// `\definecolor{name}{model}{spec}`.
137 DefineColor,
138 /// `\providecolor{name}{model}{spec}`.
139 ProvideColor,
140 /// `\colorlet{name}{base}`.
141 Colorlet,
142}
143
144/// A color-name definition site (`\definecolor{name}{model}{spec}`,
145/// `\colorlet{name}{base}`, …). The definition-side analog of [`LabelDef`],
146/// collected so color-name completion can offer document-defined colors
147/// alongside the built-in list; the reference side (`\textcolor{name}{…}`) is
148/// classified directly off the CST by completion and carries no stored record.
149#[derive(Debug, Clone, PartialEq, Eq)]
150pub struct ColorDef {
151 /// The color name, as authored (the first `{…}` group).
152 pub name: SmolStr,
153 pub kind: ColorDefKind,
154 /// Range of the command through its name group (like [`LabelDef::range`]) —
155 /// the navigation target for a future go-to-definition.
156 pub range: TextRange,
157 /// Range of just the trimmed name text inside the braces — the precise span a
158 /// future rename would rewrite.
159 pub key_range: TextRange,
160}
161
162/// A citation *use* site — one per key. A `\cite{a,b}` produces two
163/// `CitationRef`s. Citations are always cross-file: cite keys live in `.bib`
164/// files, so there is no in-file resolution (no `resolved` flag); the
165/// `undefined-citation` lint resolves them against the project's bibliography via
166/// `project::citations::ResolvedCitations` (in the `badness` crate).
167#[derive(Debug, Clone, PartialEq, Eq)]
168pub struct CitationRef {
169 /// The cite key, as authored.
170 pub name: SmolStr,
171 /// The citation command that introduced it, sans backslash (`cite`,
172 /// `parencite`, `nocite`, …) — informational, for diagnostics.
173 pub command: SmolStr,
174 /// Range of the enclosing command (shared by keys split from one `\cite{a,b}`,
175 /// like [`LabelRef::range`]).
176 pub range: TextRange,
177 /// Range of just this key inside the braces (`b` in `\cite{a,b}`), trimmed of
178 /// surrounding whitespace — the precise span a rename rewrites.
179 pub key_range: TextRange,
180}