1use super::{
2 DiagnosticLevel, STeXParseData,
3 rules::{
4 MathStructureArg, NotationArg, ParagraphArg, ProblemArg, SModuleArg, SymdeclArg, SymdefArg,
5 TextSymdeclArg, VardefArg,
6 },
7};
8use crate::quickparse::{
9 latex::{
10 Environment, FromLaTeXToken, Group, GroupState, Groups, LaTeXParser, Macro, ParserState,
11 rules::{AnyEnv, AnyMacro, DynMacro},
12 },
13 stex::rules::{IncludeProblemArg, MHGraphicsArg, SRefOptsA, SRefOptsB},
14};
15use flams_math_archives::{
16 MathArchive,
17 backend::{AnyBackend, LocalBackend},
18};
19use flams_utils::{
20 id_counters::IdCounter,
21 impossible,
22 prelude::HMap,
23 sourcerefs::{LSPLineCol, StringPosition, StringRange},
24 vecmap::{VecMap, VecSet},
25};
26use ftml_ontology::narrative::elements::{paragraphs::ParagraphKind, problems::CognitiveDimension};
27use ftml_uris::{
28 ArchiveId, ArchiveUri, DocumentElementUri, DocumentUri, DomainUri, IsDomainUri, Language,
29 ModuleUri, PathUri, SymbolUri, UriName, UriPath, UriWithArchive, UriWithPath,
30};
31use smallvec::SmallVec;
32use std::{
33 borrow::Cow,
34 collections::hash_map::Entry,
35 fmt::Write,
36 path::{Path, PathBuf},
37};
38
39#[allow(clippy::large_enum_variant)]
40#[derive(Debug, serde::Serialize)]
41pub enum STeXToken<Pos: StringPosition> {
42 ImportModule {
43 archive_range: Option<StringRange<Pos>>,
44 path_range: StringRange<Pos>,
45 module: ModuleReference,
46 full_range: StringRange<Pos>,
47 token_range: StringRange<Pos>,
48 },
49 UseModule {
50 archive_range: Option<StringRange<Pos>>,
51 path_range: StringRange<Pos>,
52 module: ModuleReference,
53 full_range: StringRange<Pos>,
54 token_range: StringRange<Pos>,
55 },
56 UseStructure {
57 structure: SymbolReference<Pos>,
58 structure_range: StringRange<Pos>,
59 full_range: StringRange<Pos>,
60 token_range: StringRange<Pos>,
61 },
62 SetMetatheory {
63 archive_range: Option<StringRange<Pos>>,
64 path_range: StringRange<Pos>,
65 module: ModuleReference,
66 full_range: StringRange<Pos>,
67 token_range: StringRange<Pos>,
68 },
69 Inputref {
70 archive: Option<(ArchiveId, StringRange<Pos>)>,
71 filepath: (std::sync::Arc<str>, StringRange<Pos>),
72 full_range: StringRange<Pos>,
73 token_range: StringRange<Pos>,
74 },
75 SRef {
76 full_range: StringRange<Pos>,
77 token_range: StringRange<Pos>,
78 opt_args: Vec<SRefOptsA<Pos, Self>>,
79 label_range: StringRange<Pos>,
80 in_opt_args: Vec<SRefOptsB<Pos, Self>>,
81 target: DocumentElementUri,
82 target_path: std::sync::Arc<Path>,
83 in_doc: Option<(DocumentUri, std::sync::Arc<Path>)>,
84 },
85 IncludeProblem {
86 filepath: (std::sync::Arc<str>, StringRange<Pos>),
87 archive: Option<(ArchiveId, StringRange<Pos>)>,
88 full_range: StringRange<Pos>,
89 token_range: StringRange<Pos>,
90 args: Vec<IncludeProblemArg<Pos>>,
91 },
92 MHGraphics {
93 filepath: (std::sync::Arc<str>, StringRange<Pos>),
94 archive: Option<(ArchiveId, StringRange<Pos>)>,
95 full_range: StringRange<Pos>,
96 token_range: StringRange<Pos>,
97 args: Vec<MHGraphicsArg<Pos>>,
98 },
99 MHInput {
100 archive: Option<(ArchiveId, StringRange<Pos>)>,
101 filepath: (std::sync::Arc<str>, StringRange<Pos>),
102 full_range: StringRange<Pos>,
103 token_range: StringRange<Pos>,
104 },
105 Module {
106 uri: ModuleUri,
107 rules: ModuleRules<Pos>,
108 name_range: StringRange<Pos>,
109 opts: Vec<SModuleArg<Pos, Self>>,
110 sig: Option<Language>,
111 meta_theory: Option<ModuleReference>,
112 full_range: StringRange<Pos>,
113 children: Vec<Self>,
114 smodule_range: StringRange<Pos>,
115 },
116 MathStructure {
117 uri: SymbolReference<Pos>,
118 extends: Vec<(SymbolReference<Pos>, StringRange<Pos>)>,
119 name_range: StringRange<Pos>,
120 opts: Vec<MathStructureArg<Pos, Self>>,
121 full_range: StringRange<Pos>,
122 children: Vec<Self>,
123 mathstructure_range: StringRange<Pos>,
124 },
125 ConservativeExt {
126 uri: SymbolReference<Pos>,
127 ext_range: StringRange<Pos>,
128 full_range: StringRange<Pos>,
129 children: Vec<Self>,
130 extstructure_range: StringRange<Pos>,
131 },
132 MorphismEnv {
133 full_range: StringRange<Pos>,
134 env_range: StringRange<Pos>,
135 name_range: StringRange<Pos>,
136 uri: SymbolUri,
137 star: bool,
138 domain: ModuleOrStruct<Pos>,
139 domain_range: StringRange<Pos>,
140 kind: MorphismKind,
141 children: Vec<Self>,
142 },
143 InlineMorphism {
144 full_range: StringRange<Pos>,
145 token_range: StringRange<Pos>,
146 name_range: StringRange<Pos>,
147 uri: SymbolUri,
148 star: bool,
149 domain: ModuleOrStruct<Pos>,
150 domain_range: StringRange<Pos>,
151 kind: MorphismKind,
152 assignments: Vec<InlineMorphAssign<Pos, Self>>,
153 },
154 Paragraph {
155 kind: ParagraphKind,
156 full_range: StringRange<Pos>,
157 name_range: StringRange<Pos>,
158 symbol: Option<SymbolReference<Pos>>,
159 parsed_args: Vec<ParagraphArg<Pos, Self>>,
160 children: Vec<Self>,
161 },
162 Problem {
163 sub: bool,
164 full_range: StringRange<Pos>,
165 name_range: StringRange<Pos>,
166 parsed_args: Vec<ProblemArg<Pos, Self>>,
167 children: Vec<Self>,
168 },
169 InlineParagraph {
170 kind: ParagraphKind,
171 full_range: StringRange<Pos>,
172 token_range: StringRange<Pos>,
173 symbol: Option<SymbolReference<Pos>>,
174 parsed_args: Vec<ParagraphArg<Pos, Self>>,
175 children: Vec<Self>,
176 children_range: StringRange<Pos>,
177 },
178 #[allow(clippy::type_complexity)]
179 Symdecl {
180 uri: SymbolReference<Pos>,
181 main_name_range: StringRange<Pos>,
182 full_range: StringRange<Pos>,
183 parsed_args: Vec<SymdeclArg<Pos, Self>>,
184 token_range: StringRange<Pos>,
185 starred: bool,
186 },
187 #[allow(clippy::type_complexity)]
188 TextSymdecl {
189 uri: SymbolReference<Pos>,
190 main_name_range: StringRange<Pos>,
191 full_range: StringRange<Pos>,
192 parsed_args: Vec<TextSymdeclArg<Pos, Self>>,
193 token_range: StringRange<Pos>,
194 },
195 Notation {
196 uri: SmallVec<SymbolReference<Pos>, 1>,
197 token_range: StringRange<Pos>,
198 name_range: StringRange<Pos>,
199 notation_args: Vec<NotationArg<Pos, Self>>,
200 full_range: StringRange<Pos>,
201 },
202 RenameDecl {
203 uri: SymbolReference<Pos>,
204 token_range: StringRange<Pos>,
205 orig_range: StringRange<Pos>,
206 name_range: Option<StringRange<Pos>>,
207 macroname_range: StringRange<Pos>,
208 full_range: StringRange<Pos>,
209 },
210 Assign {
211 uri: SymbolReference<Pos>,
212 token_range: StringRange<Pos>,
213 orig_range: StringRange<Pos>,
214 full_range: StringRange<Pos>,
215 },
216 #[allow(clippy::type_complexity)]
217 Symdef {
218 uri: SymbolReference<Pos>,
219 main_name_range: StringRange<Pos>,
220 full_range: StringRange<Pos>,
221 parsed_args: Vec<SymdefArg<Pos, Self>>,
222 token_range: StringRange<Pos>,
223 },
224 #[allow(clippy::type_complexity)]
225 Vardef {
226 name: UriName,
227 main_name_range: StringRange<Pos>,
228 full_range: StringRange<Pos>,
229 parsed_args: Vec<VardefArg<Pos, Self>>,
230 token_range: StringRange<Pos>,
231 },
232 #[allow(clippy::type_complexity)]
233 Varseq {
234 name: UriName,
235 main_name_range: StringRange<Pos>,
236 full_range: StringRange<Pos>,
237 parsed_args: Vec<VardefArg<Pos, Self>>,
238 token_range: StringRange<Pos>,
239 },
240 SemanticMacro {
241 uri: SymbolReference<Pos>,
242 argnum: u8,
243 full_range: StringRange<Pos>,
244 token_range: StringRange<Pos>,
245 },
246 VariableMacro {
247 name: UriName,
248 orig: StringRange<Pos>,
249 argnum: u8,
250 sequence: bool,
251 full_range: StringRange<Pos>,
252 token_range: StringRange<Pos>,
253 },
254 SymName {
255 uri: SmallVec<SymbolReference<Pos>, 1>,
256 is_def: bool,
257 full_range: StringRange<Pos>,
258 token_range: StringRange<Pos>,
259 name_range: StringRange<Pos>,
260 mode: SymnameMode<Pos>,
261 },
262 Symuse {
263 uri: SmallVec<SymbolReference<Pos>, 1>,
264 full_range: StringRange<Pos>,
265 token_range: StringRange<Pos>,
266 name_range: StringRange<Pos>,
267 },
268 Definiens {
269 uri: SmallVec<SymbolReference<Pos>, 1>,
270 full_range: StringRange<Pos>,
271 token_range: StringRange<Pos>,
272 name_range: Option<StringRange<Pos>>,
273 },
274 Defnotation {
275 full_range: StringRange<Pos>,
276 },
277 Svar {
278 name: UriName,
279 full_range: StringRange<Pos>,
280 token_range: StringRange<Pos>,
281 name_range: Option<StringRange<Pos>>,
282 arg_range: StringRange<Pos>,
283 },
284 Symref {
285 uri: SmallVec<SymbolReference<Pos>, 1>,
286 is_def: bool,
287 full_range: StringRange<Pos>,
288 token_range: StringRange<Pos>,
289 name_range: StringRange<Pos>,
290 text: (StringRange<Pos>, Vec<Self>),
291 },
292 Precondition {
293 uri: SmallVec<SymbolReference<Pos>, 1>,
294 full_range: StringRange<Pos>,
295 token_range: StringRange<Pos>,
296 dim_range: StringRange<Pos>,
297 symbol_range: StringRange<Pos>,
298 dim: CognitiveDimension,
299 },
300 Objective {
301 uri: SmallVec<SymbolReference<Pos>, 1>,
302 full_range: StringRange<Pos>,
303 token_range: StringRange<Pos>,
304 dim_range: StringRange<Pos>,
305 symbol_range: StringRange<Pos>,
306 dim: CognitiveDimension,
307 },
308 SnifySuggestion {
309 range: StringRange<Pos>,
310 symbols: SmallVec<(SymbolUri, bool), 1>,
311 },
312 Vec(Vec<Self>),
313}
314
315impl<'a, P: StringPosition> FromLaTeXToken<'a, P> for STeXToken<P> {
316 fn from_comment(_: StringRange<P>) -> Option<Self> {
317 None
318 }
319 fn from_group(_: StringRange<P>, v: Vec<Self>) -> Option<Self> {
320 Some(Self::Vec(v))
321 }
322 fn from_math(_: bool, _: StringRange<P>, v: Vec<Self>) -> Option<Self> {
323 Some(Self::Vec(v))
324 }
325 fn from_control_sequence(_: P, _: &'a str) -> Option<Self> {
326 None
327 }
328 fn from_text(_: StringRange<P>, _: &'a str) -> Option<Self> {
329 None
330 }
331 fn from_macro_application(_: Macro<'a, P>) -> Option<Self> {
332 None
333 }
334 fn from_environment(e: Environment<'a, P, Self>) -> Option<Self> {
335 Some(Self::Vec(e.children))
336 }
337}
338
339#[derive(Copy, Clone, Debug, serde::Serialize)]
340pub enum MorphismKind {
341 CopyModule,
342 InterpretModule,
343}
344
345#[derive(Debug, Clone, serde::Serialize)]
346pub enum SymnameMode<Pos: StringPosition> {
347 Cap {
348 post: Option<(StringRange<Pos>, StringRange<Pos>, String)>,
349 },
350 PostS {
351 pre: Option<(StringRange<Pos>, StringRange<Pos>, String)>,
352 },
353 CapAndPostS,
354 PrePost {
355 pre: Option<(StringRange<Pos>, StringRange<Pos>, String)>,
356 post: Option<(StringRange<Pos>, StringRange<Pos>, String)>,
357 },
358}
359impl<Pos: StringPosition> SymnameMode<Pos> {
360 pub fn apply<'s>(&'s self, s: &'s str) -> impl std::fmt::Display + 's {
361 struct Disp<'s, Pos: StringPosition> {
362 sn: &'s SymnameMode<Pos>,
363 txt: &'s str,
364 }
365 impl<Pos: StringPosition> std::fmt::Display for Disp<'_, Pos> {
366 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367 match self.sn {
368 SymnameMode::Cap {
369 post: Some((_, _, post)),
370 } => {
371 if self.txt.is_empty() {
372 return post.fmt(f);
373 }
374 let cap = unsafe { self.txt.chars().next().unwrap_unchecked() };
376 for c in cap.to_uppercase() {
377 f.write_char(c)?;
378 }
379 write!(f, "{}{post}", &self.txt[cap.len_utf8()..])
380 }
381 SymnameMode::Cap { .. } => {
382 if self.txt.is_empty() {
383 return Ok(());
384 }
385 let cap = unsafe { self.txt.chars().next().unwrap_unchecked() };
387 for c in cap.to_uppercase() {
388 f.write_char(c)?;
389 }
390 self.txt[cap.len_utf8()..].fmt(f)
391 }
392 SymnameMode::PostS {
393 pre: Some((_, _, pre)),
394 } => write!(f, "{pre}{}s", self.txt),
395 SymnameMode::PostS { .. } => write!(f, "{}s", self.txt),
396 SymnameMode::CapAndPostS => {
397 if self.txt.is_empty() {
398 return Ok(());
399 }
400 let cap = unsafe { self.txt.chars().next().unwrap_unchecked() };
402 for c in cap.to_uppercase() {
403 f.write_char(c)?;
404 }
405 write!(f, "{}s", &self.txt[cap.len_utf8()..])
406 }
407 SymnameMode::PrePost {
408 pre: Some((_, _, pre)),
409 post: Some((_, _, post)),
410 } => write!(f, "{pre}{}{post}", self.txt),
411 SymnameMode::PrePost {
412 pre: Some((_, _, pre)),
413 ..
414 } => write!(f, "{pre}{}", self.txt),
415 SymnameMode::PrePost {
416 post: Some((_, _, post)),
417 ..
418 } => write!(f, "{}{post}", self.txt),
419 _ => self.txt.fmt(f),
420 }
421 }
422 }
423 Disp { sn: self, txt: s }
424 }
425 pub fn make_cow<'s>(&self, s: &'s str) -> Cow<'s, str> {
426 match self {
427 Self::PrePost {
428 pre: None,
429 post: None,
430 } => Cow::Borrowed(s),
431 _ => Cow::Owned(self.apply(s).to_string()),
432 }
433 }
434}
435
436#[derive(Debug, Clone, serde::Serialize)]
437pub struct InlineMorphAssign<Pos: StringPosition, T> {
438 pub symbol: SymbolReference<Pos>,
439 pub symbol_range: StringRange<Pos>,
440 pub first: Option<(Pos, InlineMorphAssKind<Pos, T>)>,
441 pub second: Option<(Pos, InlineMorphAssKind<Pos, T>)>,
442}
443
444impl<Pos: StringPosition, T1> InlineMorphAssign<Pos, T1> {
445 pub fn into_other<T2>(
446 self,
447 mut cont: impl FnMut(Vec<T1>) -> Vec<T2>,
448 ) -> InlineMorphAssign<Pos, T2> {
449 let Self {
450 symbol,
451 symbol_range,
452 first,
453 second,
454 } = self;
455 InlineMorphAssign {
456 symbol,
457 symbol_range,
458 first: first.map(|(p, k)| {
459 (
460 p,
461 match k {
462 InlineMorphAssKind::Rename(a, b, c) => InlineMorphAssKind::Rename(a, b, c),
463 InlineMorphAssKind::Df(v) => InlineMorphAssKind::Df(cont(v)),
464 },
465 )
466 }),
467 second: second.map(|(p, k)| {
468 (
469 p,
470 match k {
471 InlineMorphAssKind::Rename(a, b, c) => InlineMorphAssKind::Rename(a, b, c),
472 InlineMorphAssKind::Df(v) => InlineMorphAssKind::Df(cont(v)),
473 },
474 )
475 }),
476 }
477 }
478}
479
480pub struct InlineMorphAssIter<'a, Pos: StringPosition, T>(
481 std::slice::Iter<'a, InlineMorphAssign<Pos, T>>,
482 Option<std::slice::Iter<'a, T>>,
483);
484impl<'a, Pos: StringPosition, T> InlineMorphAssIter<'a, Pos, T> {
485 pub fn new(v: &'a [InlineMorphAssign<Pos, T>]) -> Self {
486 Self(v.iter(), None)
487 }
488}
489impl<'a, Pos: StringPosition, T> Iterator for InlineMorphAssIter<'a, Pos, T> {
490 type Item = &'a T;
491 fn next(&mut self) -> Option<Self::Item> {
492 loop {
493 if let Some(n) = &mut self.1 {
494 if let Some(n) = n.next() {
495 return Some(n);
496 }
497 }
498 if let Some(a) = self.0.next() {
499 if let Some((_, InlineMorphAssKind::Df(v))) = &a.first {
500 self.1 = Some(v.iter());
501 continue;
502 }
503 if let Some((_, InlineMorphAssKind::Df(v))) = &a.second {
504 self.1 = Some(v.iter());
505 }
506 } else {
507 return None;
508 }
509 }
510 }
511}
512
513#[derive(Debug, Clone, serde::Serialize)]
514pub enum InlineMorphAssKind<Pos: StringPosition, T> {
515 Df(Vec<T>),
516 Rename(
517 Option<(UriName, StringRange<Pos>)>,
518 Box<str>,
519 StringRange<Pos>,
520 ),
521}
522
523#[derive(Debug, Clone, serde::Serialize)]
524pub struct SymbolReference<Pos: StringPosition> {
525 pub uri: SymbolUri,
526 pub filepath: Option<std::sync::Arc<Path>>,
527 pub range: StringRange<Pos>,
528}
529
530#[derive(Debug, Clone, serde::Serialize)]
531pub struct ModuleReference {
532 pub uri: ModuleUri,
533 pub in_doc: DocumentUri,
534 pub rel_path: Option<std::sync::Arc<str>>,
535 pub full_path: Option<std::sync::Arc<Path>>,
536}
537impl ModuleReference {
538 }
559
560pub enum GetModuleError {
561 NotFound(ModuleUri),
562 Cycle(Vec<DocumentUri>),
563}
564impl std::fmt::Display for GetModuleError {
565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 match self {
567 Self::NotFound(uri) => write!(f, "module not found: {uri}"),
568 Self::Cycle(cycle) => write!(
569 f,
570 "cycle in module dependencies: {}",
571 cycle
572 .iter()
573 .map(DocumentUri::to_string)
574 .collect::<Vec<_>>()
575 .join(" -> ")
576 ),
577 }
578 }
579}
580
581#[allow(unused_variables)]
582pub trait STeXModuleStore {
583 const FULL: bool;
584 fn get_module(
586 &mut self,
587 module: &ModuleReference,
588 in_path: Option<&std::sync::Arc<Path>>,
589 ) -> Result<STeXParseData, GetModuleError>;
590 #[inline]
591 fn add_text<Pos: StringPosition>(
592 &self,
593 r: StringRange<Pos>,
594 text: &str,
595 language: Language,
596 needs_usemodule: &dyn Fn(&SymbolUri) -> bool,
597 ) -> Option<STeXToken<Pos>> {
598 None
599 }
600 fn add_verbalization(&mut self, s: &str, symbol: &SymbolUri, language: Language) {}
601}
602impl STeXModuleStore for () {
603 const FULL: bool = false;
604 #[inline]
605 fn get_module(
606 &mut self,
607 r: &ModuleReference,
608 _: Option<&std::sync::Arc<Path>>,
609 ) -> Result<STeXParseData, GetModuleError> {
610 Err(GetModuleError::NotFound(r.uri.clone()))
611 }
612}
613
614#[derive(Debug, serde::Serialize)]
615pub enum ModuleRule<Pos: StringPosition> {
616 Import(ModuleReference),
617 Symbol(SymbolRule<Pos>),
618 Structure {
619 symbol: SymbolRule<Pos>,
620 rules: ModuleRules<Pos>,
622 },
623 ConservativeExt(SymbolReference<Pos>, ModuleRules<Pos>),
624 StructureImport(SymbolReference<Pos>),
625}
626
627#[derive(Debug, Clone, serde::Serialize)]
628pub struct SymbolRule<Pos: StringPosition> {
629 pub uri: SymbolReference<Pos>,
630 pub macroname: Option<std::sync::Arc<str>>,
631 pub has_tp: bool,
632 pub has_df: bool,
633 pub argnum: u8,
634}
635impl<Pos: StringPosition> SymbolRule<Pos> {
636 fn as_rule<'a, MS: STeXModuleStore>(
637 &self,
638 ) -> Option<(
639 Cow<'a, str>,
640 AnyMacro<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>>,
641 )> {
642 self.macroname.as_ref().map(|m| {
643 (
644 m.to_string().into(),
645 AnyMacro::Ext(DynMacro {
646 ptr: super::rules::semantic_macro as _,
647 arg: MacroArg::Symbol(self.uri.clone(), self.argnum),
648 }),
649 )
650 })
651 }
652}
653impl<Pos: StringPosition> Eq for SymbolReference<Pos> {}
654impl<Pos: StringPosition> PartialEq for SymbolReference<Pos> {
655 #[inline]
656 fn eq(&self, other: &Self) -> bool {
657 self.uri == other.uri
658 }
659}
660
661#[derive(Debug, Clone, serde::Serialize)]
662pub struct ModuleRules<Pos: StringPosition> {
663 pub rules: std::sync::Arc<[ModuleRule<Pos>]>,
664}
665impl<Pos: StringPosition> Default for ModuleRules<Pos> {
666 #[inline]
667 fn default() -> Self {
668 Self {
669 rules: std::sync::Arc::new([]),
670 }
671 }
672}
673
674pub struct STeXParseState<'a, Pos: StringPosition, MS: STeXModuleStore> {
675 pub(super) archive: Option<&'a ArchiveUri>,
676 pub(super) in_path: Option<std::sync::Arc<Path>>,
677 pub(super) doc_uri: &'a DocumentUri,
678 pub(super) backend: &'a AnyBackend,
679 pub(super) language: Language,
680 pub(super) dependencies: Vec<std::sync::Arc<Path>>,
681 pub(super) modules: SmallVec<(ModuleUri, ModuleRules<Pos>), 1>,
682 pub(super) module_store: MS,
683 name_counter: IdCounter,
684}
685impl<'a, MS: STeXModuleStore> STeXParseState<'a, LSPLineCol, MS> {
686 fn load_module(
687 &mut self,
688 module: &ModuleReference,
689 ) -> Result<ModuleRules<LSPLineCol>, GetModuleError> {
690 for (uri, m) in &self.modules {
691 if *uri == module.uri {
692 return Ok(m.clone());
693 }
694 }
695 match self.module_store.get_module(module, self.in_path.as_ref()) {
699 Ok(d) => {
700 for (uri, m) in &d.lock().modules {
701 if *uri == module.uri {
702 return Ok(m.clone());
703 }
704 }
705 Err(GetModuleError::NotFound(module.uri.clone()))
706 }
707 Err(e) => Err(e),
708 }
709 }
710
711 fn load_rules(
712 mod_ref: ModuleReference,
713 irules: ModuleRules<LSPLineCol>,
714 prev: &[STeXGroup<'a, MS, LSPLineCol>],
715 current: &mut HMap<Cow<'a, str>, AnyMacro<'a, LSPLineCol, STeXToken<LSPLineCol>, Self>>,
716 changes: &mut HMap<
717 Cow<'a, str>,
718 Option<AnyMacro<'a, LSPLineCol, STeXToken<LSPLineCol>, Self>>,
719 >,
720 semantic_rules: &mut Vec<SemanticRule<LSPLineCol>>,
721 f: &mut impl FnMut(&ModuleReference) -> Option<ModuleRules<LSPLineCol>>,
722 cycles_count: u16,
723 ) -> Result<(), ()> {
724 if cycles_count >= 500 {
725 return Err(());
726 }
727 if Self::has_module(prev, semantic_rules, &mod_ref) {
728 return Ok(());
729 }
730 for rule in irules.rules.iter() {
731 match rule {
732 ModuleRule::Import(m) => {
733 if let Some(rls) = f(m) {
734 Self::load_rules(
735 m.clone(),
736 rls.clone(),
737 prev,
738 current,
739 changes,
740 semantic_rules,
741 f,
742 cycles_count + 1,
743 )?;
744 }
745 }
746 ModuleRule::Symbol(rule) if MS::FULL => {
747 if let Some((name, rule)) = rule.as_rule() {
749 let old = current.insert(name.clone(), rule);
750 if let Entry::Vacant(e) = changes.entry(name) {
751 e.insert(old);
752 }
753 }
754 }
755 ModuleRule::Structure { symbol, rules } => {
756 semantic_rules.push(SemanticRule::Structure {
757 symbol: symbol.clone(),
758 rules: rules.clone(),
759 });
760 if MS::FULL {
761 if let Some((name, rule)) = symbol.as_rule() {
762 let old = current.insert(name.clone(), rule);
763 if let Entry::Vacant(e) = changes.entry(name) {
764 e.insert(old);
765 }
766 }
767 }
768 }
769 ModuleRule::ConservativeExt(s, rls) => {
770 semantic_rules.push(SemanticRule::ConservativeExt(s.clone(), rls.clone()));
771 }
772 _ => (),
773 }
774 }
775 semantic_rules.push(SemanticRule::Module(mod_ref, irules));
776 Ok(())
777 }
778
779 fn has_module(
780 prev: &[STeXGroup<'a, MS, LSPLineCol>],
781 current: &Vec<SemanticRule<LSPLineCol>>,
782 mod_ref: &ModuleReference,
783 ) -> bool {
784 if current
785 .iter()
786 .any(|e| matches!(e,SemanticRule::Module(r,_) if r.uri == mod_ref.uri))
787 {
788 return true;
789 }
790 for p in prev.iter().rev() {
791 if matches!(&p.kind,GroupKind::Module { uri, .. } if *uri == mod_ref.uri) {
792 return true;
793 }
794 if p.semantic_rules
795 .iter()
796 .any(|e| matches!(e,SemanticRule::Module(r,_) if r.uri == mod_ref.uri))
797 {
798 return true;
799 }
800 }
801 false
802 }
803
804 #[allow(clippy::needless_pass_by_value)]
806 pub fn add_use(
807 &mut self,
808 module: &ModuleReference,
809 groups: Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
810 range: StringRange<LSPLineCol>,
811 ) {
812 let groups_ls = &mut **groups.groups;
813 assert!(!groups_ls.is_empty());
814 let i = groups_ls.len() - 1;
815 let (prev, after) = groups_ls.split_at_mut(i);
816 let prev = &*prev;
817 let g = &mut after[0];
818 match self.load_module(module) {
819 Ok(irules) => {
820 if Self::load_rules(
821 module.clone(),
822 irules,
823 prev,
824 groups.rules,
825 &mut g.inner.macro_rule_changes,
826 &mut g.semantic_rules,
827 &mut |m| match self.load_module(m) {
828 Ok(r) => Some(r),
829 Err(e) => {
830 groups
831 .tokenizer
832 .problem(range.start, e, DiagnosticLevel::Error);
833 None
834 }
835 },
836 0,
837 )
838 .is_err()
839 {
840 groups
841 .tokenizer
842 .problem(range.start, "Import cycle", DiagnosticLevel::Error);
843 }
844 }
845 Err(e) => groups
846 .tokenizer
847 .problem(range.start, e, DiagnosticLevel::Error),
848 }
849 }
850
851 fn has_structure(
852 prev: &[STeXGroup<'a, MS, LSPLineCol>],
853 current: &Vec<SemanticRule<LSPLineCol>>,
854 sym_ref: &SymbolReference<LSPLineCol>,
855 ) -> bool {
856 if current
857 .iter()
858 .any(|e| matches!(e,SemanticRule::StructureImport(r,_) if r.uri == sym_ref.uri))
859 {
860 return true;
861 }
862 for p in prev.iter().rev() {
863 if p.semantic_rules
864 .iter()
865 .any(|e| matches!(e,SemanticRule::StructureImport(r,_) if r.uri == sym_ref.uri))
866 {
867 return true;
868 }
869 }
870 false
871 }
872 fn load_structure(
873 symbol: &SymbolReference<LSPLineCol>,
874 prev: &[STeXGroup<'a, MS, LSPLineCol>],
875 semantic_rules: &Vec<SemanticRule<LSPLineCol>>,
876 ) -> Option<ModuleRules<LSPLineCol>> {
877 for r in semantic_rules.iter().rev() {
878 match r {
879 SemanticRule::Structure {
880 symbol: isymbol,
881 rules,
882 ..
883 } if isymbol.uri.uri == symbol.uri => return Some(rules.clone()),
884 _ => (),
885 }
886 }
887 for g in prev.iter().rev() {
888 for r in g.semantic_rules.iter().rev() {
889 match r {
890 SemanticRule::Structure {
891 symbol: isymbol,
892 rules,
893 ..
894 } if isymbol.uri.uri == symbol.uri => return Some(rules.clone()),
895 _ => (),
896 }
897 }
898 }
899 None
900 }
901
902 fn load_structure_rules(
903 symbol: SymbolReference<LSPLineCol>,
904 irules: ModuleRules<LSPLineCol>,
905 prev: &[STeXGroup<'a, MS, LSPLineCol>],
906 current: &mut HMap<Cow<'a, str>, AnyMacro<'a, LSPLineCol, STeXToken<LSPLineCol>, Self>>,
907 changes: &mut HMap<
908 Cow<'a, str>,
909 Option<AnyMacro<'a, LSPLineCol, STeXToken<LSPLineCol>, Self>>,
910 >,
911 semantic_rules: &mut Vec<SemanticRule<LSPLineCol>>,
912 ) {
913 macro_rules! do_rule {
914 ($rule:ident) => {
915 match $rule {
916 ModuleRule::StructureImport(m)
917 if !Self::has_structure(prev, semantic_rules, &symbol) =>
918 {
919 if let Some(rls) = Self::load_structure(m, prev, semantic_rules) {
920 Self::load_structure_rules(
921 m.clone(),
922 rls,
923 prev,
924 current,
925 changes,
926 semantic_rules,
927 );
928 }
929 }
930 ModuleRule::Symbol(rule) if MS::FULL => {
931 if let Some((name, rule)) = rule.as_rule() {
933 let old = current.insert(name.clone(), rule);
934 if let Entry::Vacant(e) = changes.entry(name) {
935 e.insert(old);
936 }
937 }
938 }
939 ModuleRule::Structure { symbol, rules } => {
940 semantic_rules.push(SemanticRule::Structure {
941 symbol: symbol.clone(),
942 rules: rules.clone(),
943 });
944 if MS::FULL {
945 if let Some((name, rule)) = symbol.as_rule() {
946 let old = current.insert(name.clone(), rule);
947 if let Entry::Vacant(e) = changes.entry(name) {
948 e.insert(old);
949 }
950 }
951 }
952 }
953 _ => (),
954 }
955 };
956 }
957 for rule in irules.rules.iter() {
958 do_rule!(rule);
959 }
960 for g in prev.iter().rev() {
961 for rule in g.semantic_rules.iter().rev() {
962 if let SemanticRule::ConservativeExt(s, rls) = rule {
963 if s.uri == symbol.uri {
965 for rule in rls.rules.iter() {
966 do_rule!(rule);
967 }
968 }
969 }
970 }
971 }
972 semantic_rules.push(SemanticRule::StructureImport(symbol, irules));
973 }
974
975 pub fn import_structure(
976 &mut self,
977 symbol: &SymbolReference<LSPLineCol>,
978 srules: &ModuleRules<LSPLineCol>,
979 groups: &mut Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
980 range: StringRange<LSPLineCol>,
981 ) {
982 let groups_ls = &mut **groups.groups;
983 let Some(i) = groups_ls.iter().enumerate().rev().find_map(|(i, g)| {
984 if matches!(
985 &g.kind,
986 GroupKind::Module { .. } | GroupKind::MathStructure { .. }
987 ) {
988 Some(i)
989 } else {
990 None
991 }
992 }) else {
993 groups.tokenizer.problem(
994 range.start,
995 "\\importmodule is only allowed in a module".to_string(),
996 DiagnosticLevel::Error,
997 );
998 return;
999 };
1000 let (prev, after) = groups_ls.split_at_mut(i);
1001 let prev = &*prev;
1002 let g = &mut after[0];
1003 let (GroupKind::Module { rules, .. } | GroupKind::MathStructure { rules, .. }) =
1004 &mut g.kind
1005 else {
1006 impossible!()
1007 };
1008 if rules
1009 .iter()
1010 .any(|r| matches!(r,ModuleRule::StructureImport(s) if s.uri == symbol.uri))
1011 {
1012 return;
1013 }
1014 rules.push(ModuleRule::StructureImport(symbol.clone()));
1015 if !Self::has_structure(prev, &g.semantic_rules, &symbol) {
1016 Self::load_structure_rules(
1018 symbol.clone(),
1019 srules.clone(),
1020 prev,
1021 groups.rules,
1022 &mut g.inner.macro_rule_changes,
1023 &mut g.semantic_rules,
1024 );
1025 }
1027 }
1028
1029 pub fn use_structure(
1030 &mut self,
1031 symbol: &SymbolReference<LSPLineCol>,
1032 srules: &ModuleRules<LSPLineCol>,
1033 groups: &mut Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1034 _range: StringRange<LSPLineCol>,
1035 ) {
1036 let groups_ls = &mut **groups.groups;
1037 let i = groups_ls.len() - 1;
1038 let (prev, after) = groups_ls.split_at_mut(i);
1039 let prev = &*prev;
1040 let g = &mut after[0];
1041 if !Self::has_structure(prev, &g.semantic_rules, &symbol) {
1042 Self::load_structure_rules(
1044 symbol.clone(),
1045 srules.clone(),
1046 prev,
1047 groups.rules,
1048 &mut g.inner.macro_rule_changes,
1049 &mut g.semantic_rules,
1050 );
1051 }
1053 }
1054
1055 #[allow(clippy::needless_pass_by_value)]
1056 pub fn add_import(
1057 &mut self,
1058 module: &ModuleReference,
1059 groups: Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1060 range: StringRange<LSPLineCol>,
1061 ) {
1062 let groups_ls = &mut **groups.groups;
1063 let Some(i) = groups_ls.iter().enumerate().rev().find_map(|(i, g)| {
1064 if matches!(
1065 &g.kind,
1066 GroupKind::Module { .. } | GroupKind::MathStructure { .. }
1067 ) {
1068 Some(i)
1069 } else {
1070 None
1071 }
1072 }) else {
1073 groups.tokenizer.problem(
1074 range.start,
1075 "\\importmodule is only allowed in a module".to_string(),
1076 DiagnosticLevel::Error,
1077 );
1078 return;
1079 };
1080 let (prev, after) = groups_ls.split_at_mut(i);
1081 let prev = &*prev;
1082 let g = &mut after[0];
1083 let (GroupKind::Module { rules, .. } | GroupKind::MathStructure { rules, .. }) =
1084 &mut g.kind
1085 else {
1086 unreachable!()
1087 };
1088 if rules
1089 .iter()
1090 .any(|r| matches!(r,ModuleRule::Import(m) if m.uri == module.uri))
1091 {
1092 return;
1093 }
1094 rules.push(ModuleRule::Import(module.clone()));
1095 match self.load_module(module) {
1096 Ok(irules) => {
1097 if Self::load_rules(
1098 module.clone(),
1099 irules,
1100 prev,
1101 groups.rules,
1102 &mut g.inner.macro_rule_changes,
1103 &mut g.semantic_rules,
1104 &mut |m| match self.load_module(m) {
1105 Ok(r) => Some(r),
1106 Err(e) => {
1107 groups
1108 .tokenizer
1109 .problem(range.start, e, DiagnosticLevel::Error);
1110 None
1111 }
1112 },
1113 0,
1114 )
1115 .is_err()
1116 {
1117 groups
1118 .tokenizer
1119 .problem(range.start, "Import cycle", DiagnosticLevel::Error);
1120 }
1121 }
1122 Err(e) => groups
1123 .tokenizer
1124 .problem(range.start, e, DiagnosticLevel::Error),
1125 }
1126 }
1127
1128 #[allow(clippy::unused_self)]
1129 fn get_symbol_macro_or_name(
1130 &self,
1131 groups: &Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1132 namestr: &str,
1133 ) -> Option<SmallVec<SymbolReference<LSPLineCol>, 1>> {
1134 let mut ret = SmallVec::new();
1135 for g in groups.groups.iter().rev() {
1136 for r in g.semantic_rules.iter().rev() {
1137 match r {
1138 SemanticRule::Symbol(r) | SemanticRule::Structure { symbol: r, .. } => {
1139 if r.macroname.as_ref().is_some_and(|n| &**n == namestr) {
1140 if !ret.contains(&r.uri) {
1141 ret.push(r.uri.clone());
1142 }
1143 continue;
1144 }
1145 if r.uri.uri.name().last() == namestr {
1146 if !ret.contains(&r.uri) {
1147 ret.push(r.uri.clone());
1148 }
1149 }
1150 }
1151 SemanticRule::Module(_, r) | SemanticRule::StructureImport(_, r) => {
1152 for r in r.rules.iter().rev() {
1153 match r {
1154 ModuleRule::Symbol(r) | ModuleRule::Structure { symbol: r, .. } => {
1155 if r.macroname.as_ref().is_some_and(|n| &**n == namestr) {
1156 if !ret.contains(&r.uri) {
1157 ret.push(r.uri.clone());
1158 }
1159 continue;
1160 }
1161 if r.uri.uri.name().last() == namestr {
1162 if !ret.contains(&r.uri) {
1163 ret.push(r.uri.clone());
1164 }
1165 }
1166 }
1167 _ => (),
1168 }
1169 }
1170 }
1171 SemanticRule::ConservativeExt(s, rls)
1172 if Self::has_structure(&groups.groups, &Vec::new(), s) =>
1173 {
1174 for r in rls.rules.iter().rev() {
1175 match r {
1176 ModuleRule::Symbol(r) | ModuleRule::Structure { symbol: r, .. } => {
1177 if r.macroname.as_ref().is_some_and(|n| &**n == namestr) {
1178 if !ret.contains(&r.uri) {
1179 ret.push(r.uri.clone());
1180 }
1181 continue;
1182 }
1183 if r.uri.uri.name().last() == namestr {
1184 if !ret.contains(&r.uri) {
1185 ret.push(r.uri.clone());
1186 }
1187 }
1188 }
1189 _ => (),
1190 }
1191 }
1192 }
1193 SemanticRule::ConservativeExt(..) => (),
1194 }
1195 }
1196 }
1197 if ret.is_empty() { None } else { Some(ret) }
1198 }
1199
1200 #[allow(clippy::unused_self)]
1201 fn get_structure_macro_or_name(
1202 &self,
1203 groups: &Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1204 namestr: &str,
1205 ) -> Option<(SymbolReference<LSPLineCol>, ModuleRules<LSPLineCol>)> {
1206 for g in groups.groups.iter().rev() {
1207 for r in g.semantic_rules.iter().rev() {
1208 match r {
1209 SemanticRule::Structure { symbol, rules, .. } => {
1210 if symbol.macroname.as_ref().is_some_and(|n| &**n == namestr) {
1211 return Some((symbol.uri.clone(), rules.clone()));
1212 }
1213 if symbol.uri.uri.name().last() == namestr {
1214 return Some((symbol.uri.clone(), rules.clone()));
1215 }
1216 }
1217 SemanticRule::Module(_, r) => {
1218 for r in r.rules.iter().rev() {
1219 match r {
1220 ModuleRule::Structure { symbol, rules, .. } => {
1221 if symbol.macroname.as_ref().is_some_and(|n| &**n == namestr) {
1222 return Some((symbol.uri.clone(), rules.clone()));
1223 }
1224 if symbol.uri.uri.name().last() == namestr {
1225 return Some((symbol.uri.clone(), rules.clone()));
1226 }
1227 }
1228 _ => (),
1229 }
1230 }
1231 }
1232 _ => (),
1233 }
1234 }
1235 }
1236 None
1237 }
1238
1239 fn compare(symbol: &str, module: &str, path: Option<&str>, uri: &SymbolUri) -> bool {
1240 fn compare_names(n1: &str, n2: &UriName) -> Option<bool> {
1241 let mut symbol_steps = n1.split('/').rev();
1242 let mut uri_steps = n2.steps().rev();
1243 loop {
1244 let Some(sym) = symbol_steps.next() else {
1245 return if uri_steps.next().is_some() {
1246 None
1247 } else {
1248 Some(true)
1249 };
1250 };
1251 let Some(uristep) = uri_steps.next() else {
1252 return Some(false);
1253 };
1254 if sym != uristep {
1255 if symbol_steps.next().is_none() && uristep.ends_with(sym) {
1256 return None;
1257 }
1258 return Some(false);
1259 }
1260 }
1261 }
1262 if compare_names(symbol, uri.name()) != Some(true) {
1263 return false;
1264 }
1265 match compare_names(module, uri.module_name()) {
1266 None | Some(true) if path.is_none() => return true,
1267 Some(false) | None => return false,
1268 Some(true) => (),
1269 }
1270 let Some(mut path) = path else { unreachable!() };
1271 if let Some(uri_path) = uri.path() {
1272 for step in uri_path.steps().rev() {
1273 if path.is_empty() {
1274 return true;
1275 }
1276 if let Some(p) = path.strip_suffix(step) {
1277 if let Some(p) = p.strip_suffix('/') {
1278 path = p;
1279 } else {
1280 if p.is_empty() {
1281 return true;
1282 }
1283 }
1284 } else {
1285 return false;
1286 }
1287 }
1288 }
1289 let id = uri.archive_id();
1290 return id.as_ref().ends_with(path);
1291 }
1292
1293 #[allow(clippy::unused_self)]
1294 fn get_symbol_complex(
1295 &self,
1296 groups: &Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1297 symbol: &str,
1298 module: &str,
1299 path: Option<&str>,
1300 ) -> Option<SmallVec<SymbolReference<LSPLineCol>, 1>> {
1301 let mut ret = SmallVec::new();
1302 for g in groups.groups.iter().rev() {
1303 for r in g.semantic_rules.iter().rev() {
1304 match r {
1305 SemanticRule::Symbol(r) | SemanticRule::Structure { symbol: r, .. }
1306 if Self::compare(symbol, module, path, &r.uri.uri) =>
1307 {
1308 if !ret.contains(&r.uri) {
1309 ret.push(r.uri.clone());
1310 }
1311 }
1312 SemanticRule::Module(_, r) | SemanticRule::StructureImport(_, r) => {
1313 for r in r.rules.iter().rev() {
1314 match r {
1315 ModuleRule::Symbol(r) | ModuleRule::Structure { symbol: r, .. }
1316 if Self::compare(symbol, module, path, &r.uri.uri) =>
1317 {
1318 if !ret.contains(&r.uri) {
1319 ret.push(r.uri.clone());
1320 }
1321 }
1322 _ => (),
1323 }
1324 }
1325 }
1326 SemanticRule::ConservativeExt(s, rls)
1327 if Self::has_structure(&groups.groups, &Vec::new(), s) =>
1328 {
1329 for r in rls.rules.iter().rev() {
1330 match r {
1331 ModuleRule::Symbol(r) | ModuleRule::Structure { symbol: r, .. }
1332 if Self::compare(symbol, module, path, &r.uri.uri) =>
1333 {
1334 if !ret.contains(&r.uri) {
1335 ret.push(r.uri.clone());
1336 }
1337 }
1338 _ => (),
1339 }
1340 }
1341 }
1342 _ => (),
1343 }
1344 }
1345 }
1346 if ret.is_empty() { None } else { Some(ret) }
1347 }
1348
1349 #[allow(clippy::unused_self)]
1350 fn get_structure_uri(
1351 &self,
1352 groups: &Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1353 uri: &SymbolReference<LSPLineCol>,
1354 ) -> Option<ModuleRules<LSPLineCol>> {
1355 for g in groups.groups.iter().rev() {
1356 for r in g.semantic_rules.iter().rev() {
1357 match r {
1358 SemanticRule::Structure { symbol, rules, .. } if symbol.uri.uri == uri.uri => {
1359 return Some(rules.clone());
1360 }
1361 SemanticRule::Module(_, r) => {
1362 for r in r.rules.iter().rev() {
1363 match r {
1364 ModuleRule::Structure { symbol, rules, .. }
1365 if symbol.uri.uri == uri.uri =>
1366 {
1367 return Some(rules.clone());
1368 }
1369 _ => (),
1370 }
1371 }
1372 }
1373 _ => (),
1374 }
1375 }
1376 }
1377 None
1378 }
1379
1380 #[allow(clippy::unused_self)]
1381 fn get_structure_complex(
1382 &self,
1383 groups: &Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1384 namestr: &str,
1385 module: &str,
1386 path: Option<&str>,
1387 ) -> Option<(SymbolReference<LSPLineCol>, ModuleRules<LSPLineCol>)> {
1388 for g in groups.groups.iter().rev() {
1389 for r in g.semantic_rules.iter().rev() {
1390 match r {
1391 SemanticRule::Structure { symbol, rules, .. }
1392 if Self::compare(namestr, module, path, &symbol.uri.uri) =>
1393 {
1394 return Some((symbol.uri.clone(), rules.clone()));
1395 }
1396 SemanticRule::Module(_, r) => {
1397 for r in r.rules.iter().rev() {
1398 match r {
1399 ModuleRule::Structure { symbol, rules, .. }
1400 if Self::compare(namestr, module, path, &symbol.uri.uri) =>
1401 {
1402 return Some((symbol.uri.clone(), rules.clone()));
1403 }
1404 _ => (),
1405 }
1406 }
1407 }
1408 _ => (),
1409 }
1410 }
1411 }
1412 None
1413 }
1414
1415 pub fn get_symbol(
1416 &self,
1417 start: LSPLineCol,
1418 groups: &mut Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1419 namestr: &str,
1420 ) -> Option<SmallVec<SymbolReference<LSPLineCol>, 1>> {
1421 let mut steps = namestr.split('?').rev(); let name = steps.next()?;
1424
1425 let module = if let Some(module) = steps.next() {
1426 module
1427 } else {
1428 if !name.contains('/') {
1429 let r = self.get_symbol_macro_or_name(groups, name)?;
1431 if r.len() > 1 {
1432 groups.tokenizer.problem(
1433 start,
1434 format!("Ambiguous symbol reference: {namestr}"),
1435 DiagnosticLevel::Warning,
1436 );
1437 }
1438 return Some(r);
1439 }
1440 ""
1441 };
1442 let path = if steps.next().is_none() {
1443 None
1444 } else {
1445 let i = namestr.len() - (name.len() + 1 + module.len() + 1);
1446 Some(&namestr[..i])
1447 };
1448 let r = self.get_symbol_complex(groups, name, module, path)?;
1449 if r.len() > 1 {
1450 groups.tokenizer.problem(
1451 start,
1452 format!("Ambiguous symbol reference: {namestr}"),
1453 DiagnosticLevel::Warning,
1454 );
1455 }
1456 Some(r)
1457 }
1458
1459 pub fn get_structure(
1460 &self,
1461 groups: &Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1462 namestr: &str,
1463 ) -> Option<(SymbolReference<LSPLineCol>, ModuleRules<LSPLineCol>)> {
1464 let mut steps = namestr.split('?').rev(); let name = steps.next()?;
1467
1468 let Some(module) = steps.next() else {
1469 return self.get_structure_macro_or_name(groups, name);
1470 };
1471 let path = if steps.next().is_none() {
1472 None
1473 } else {
1474 let i = namestr.len() - (name.len() + 1 + module.len() + 1);
1475 Some(&namestr[..i])
1476 };
1477 self.get_structure_complex(groups, name, module, path)
1478 }
1479
1480 #[allow(clippy::too_many_lines)]
1481 pub(super) fn resolve_module_or_struct(
1482 &mut self,
1483 groups: &Groups<'a, '_, LSPLineCol, STeXToken<LSPLineCol>, Self>,
1484 module_or_struct: &str,
1485 archive: Option<ArchiveId>,
1486 ) -> Option<(ModuleOrStruct<LSPLineCol>, Vec<ModuleRules<LSPLineCol>>)> {
1487 fn mmatch<'a, MS: STeXModuleStore>(
1488 slf: &mut STeXParseState<'a, LSPLineCol, MS>,
1489 groups: &Groups<
1490 'a,
1491 '_,
1492 LSPLineCol,
1493 STeXToken<LSPLineCol>,
1494 STeXParseState<'a, LSPLineCol, MS>,
1495 >,
1496 rules: &ModuleRules<LSPLineCol>,
1497 dones: &mut Vec<DomainUri>,
1498 target: &mut Vec<ModuleRules<LSPLineCol>>,
1499 ) -> Option<()> {
1500 for r in rules.rules.iter() {
1501 match r {
1502 ModuleRule::Import(m)
1503 if !dones
1504 .iter()
1505 .any(|u| matches!(u,DomainUri::Module(u) if *u == m.uri)) =>
1506 {
1507 load_module(slf, groups, m, dones, target)?;
1508 }
1509 ModuleRule::StructureImport(s)
1510 if !dones
1511 .iter()
1512 .any(|u| matches!(u,DomainUri::Symbol(u) if *u == s.uri)) =>
1513 {
1514 load_structure(slf, groups, s, dones, target)?;
1515 }
1516 _ => (),
1517 }
1518 }
1519 Some(())
1520 }
1521 fn load_module<'a, MS: STeXModuleStore>(
1522 slf: &mut STeXParseState<'a, LSPLineCol, MS>,
1523 groups: &Groups<
1524 'a,
1525 '_,
1526 LSPLineCol,
1527 STeXToken<LSPLineCol>,
1528 STeXParseState<'a, LSPLineCol, MS>,
1529 >,
1530 module: &ModuleReference,
1531 dones: &mut Vec<DomainUri>,
1532 target: &mut Vec<ModuleRules<LSPLineCol>>,
1533 ) -> Option<()> {
1534 dones.push(module.uri.clone().into());
1535 let rls = slf.load_module(module).ok()?;
1536 mmatch(slf, groups, &rls, dones, target)?;
1537 target.push(rls);
1538 Some(())
1539 }
1540 fn load_structure<'a, MS: STeXModuleStore>(
1541 slf: &mut STeXParseState<'a, LSPLineCol, MS>,
1542 groups: &Groups<
1543 'a,
1544 '_,
1545 LSPLineCol,
1546 STeXToken<LSPLineCol>,
1547 STeXParseState<'a, LSPLineCol, MS>,
1548 >,
1549 structure: &SymbolReference<LSPLineCol>,
1550 dones: &mut Vec<DomainUri>,
1551 target: &mut Vec<ModuleRules<LSPLineCol>>,
1552 ) -> Option<()> {
1553 dones.push(structure.uri.clone().into());
1554 let rls = slf.get_structure_uri(groups, structure)?;
1555 mmatch(slf, groups, &rls, dones, target)?;
1556 target.push(rls);
1557 Some(())
1558 }
1559 let mut dones = Vec::new();
1560 if archive.is_none() {
1561 if let Some((m, rls)) = self.find_module(module_or_struct) {
1562 let mut ret = Vec::new();
1563 let rls = rls.clone();
1564 let rf = ModuleOrStruct::Module(ModuleReference {
1565 uri: m.clone(),
1566 in_doc: self.doc_uri.clone(),
1567 rel_path: None,
1568 full_path: self.in_path.clone(),
1569 });
1570 mmatch(self, groups, &rls, &mut dones, &mut ret)?;
1571 ret.push(rls);
1572 return Some((rf, ret));
1573 }
1574 if let Some((s, r)) = self.get_structure(groups, module_or_struct) {
1575 let mut ret = Vec::new();
1576 mmatch(self, groups, &r, &mut dones, &mut ret)?;
1577 ret.push(r);
1578 return Some((ModuleOrStruct::Struct(s), ret));
1579 }
1580 }
1581 if let Some(m) = self.resolve_module(module_or_struct, archive) {
1582 let mut ret = Vec::new();
1583 load_module(self, groups, &m, &mut dones, &mut ret)?;
1584 Some((ModuleOrStruct::Module(m), ret))
1585 } else {
1586 None
1587 }
1588 }
1589}
1590
1591impl<'a, Pos: StringPosition, MS: STeXModuleStore> STeXParseState<'a, Pos, MS> {
1592 #[inline]
1593 #[must_use]
1594 pub fn new(
1595 archive: Option<&'a ArchiveUri>,
1596 in_path: Option<&'a Path>,
1597 uri: &'a DocumentUri,
1598 backend: &'a AnyBackend,
1599 on_module: MS,
1600 ) -> Self {
1601 let language = in_path.map(Language::from).unwrap_or_default();
1602 Self {
1603 archive,
1604 in_path: in_path.map(Into::into),
1605 doc_uri: uri,
1606 language,
1607 backend,
1608 modules: SmallVec::new(),
1609 module_store: on_module,
1610 name_counter: IdCounter::default(),
1611 dependencies: Vec::new(),
1612 }
1613 }
1614
1615 pub fn set_structure(
1616 &mut self,
1617 groups: &mut Groups<'a, '_, Pos, STeXToken<Pos>, Self>,
1618 rules: ModuleRules<Pos>,
1619 range: StringRange<Pos>,
1620 ) {
1621 for g in groups.groups.iter_mut().rev() {
1622 match &mut g.kind {
1623 GroupKind::Module { rules: rls, .. } => match rls.last_mut() {
1624 Some(ModuleRule::Structure {
1625 symbol,
1626 rules: rls1,
1627 ..
1628 }) => {
1629 for sr in g.semantic_rules.iter_mut().rev() {
1630 match sr {
1631 SemanticRule::Structure {
1632 symbol: symbol2,
1633 rules: rls2,
1634 ..
1635 } if symbol.uri.uri == symbol2.uri.uri => {
1636 *rls2 = rules.clone();
1637 break;
1638 }
1639 _ => (),
1640 }
1641 }
1642 *rls1 = rules;
1643 return;
1644 }
1645 Some(ModuleRule::ConservativeExt(_, rls1)) => {
1646 for sr in g.semantic_rules.iter_mut().rev() {
1647 match sr {
1648 SemanticRule::ConservativeExt(_, rls2) => {
1649 *rls2 = rules.clone();
1650 break;
1651 }
1652 _ => (),
1653 }
1654 }
1655 *rls1 = rules;
1656 return;
1657 }
1658 _ => {
1659 groups.tokenizer.problem(
1660 range.start,
1661 "mathstructure ended unexpectedly".to_string(),
1662 DiagnosticLevel::Error,
1663 );
1664 return;
1665 }
1666 },
1667 _ => (),
1668 }
1669 }
1670 groups.tokenizer.problem(
1671 range.start,
1672 "mathstructure is only allowed in a module".to_string(),
1673 DiagnosticLevel::Error,
1674 );
1675 }
1676
1677 pub fn add_structure(
1678 &mut self,
1679 groups: &mut Groups<'a, '_, Pos, STeXToken<Pos>, Self>,
1680 name: UriName,
1681 macroname: Option<std::sync::Arc<str>>,
1682 range: StringRange<Pos>,
1683 ) -> Option<SymbolReference<Pos>> {
1684 for g in groups.groups.iter_mut().rev() {
1685 match &mut g.kind {
1686 GroupKind::Module { uri, rules, .. } => {
1687 let suri = uri.clone() | name;
1688 let uri = SymbolReference {
1689 uri: suri,
1690 filepath: self.in_path.clone(),
1691 range,
1692 };
1693 for r in &*rules {
1694 match r {
1695 ModuleRule::Symbol(s) | ModuleRule::Structure { symbol: s, .. }
1696 if s.uri.uri == uri.uri =>
1697 {
1698 groups.tokenizer.problem(
1699 range.start,
1700 format!("symbol with name {} already exists", s.uri.uri),
1701 DiagnosticLevel::Warning,
1702 );
1703 }
1704 _ => (),
1705 }
1706 }
1707 self.module_store.add_verbalization(
1708 uri.uri.name.last(),
1709 &uri.uri,
1710 Language::English,
1711 );
1712 let rule = SymbolRule {
1713 uri,
1714 macroname,
1715 has_tp: false,
1716 has_df: false,
1717 argnum: 0,
1718 };
1719 if MS::FULL {
1720 if let Some((name, rule)) = rule.as_rule() {
1721 let old = groups.rules.insert(name.clone(), rule);
1722 if let Entry::Vacant(e) = g.inner.macro_rule_changes.entry(name) {
1723 e.insert(old);
1724 }
1725 }
1726 }
1727 g.semantic_rules.push(SemanticRule::Structure {
1728 symbol: rule.clone(),
1730 rules: ModuleRules::default(),
1731 });
1732 let uri = rule.uri.clone();
1733 rules.push(ModuleRule::Structure {
1734 symbol: rule,
1735 rules: ModuleRules::default(),
1736 });
1737 return Some(uri);
1738 }
1739 _ => (),
1740 }
1741 }
1742 groups.tokenizer.problem(
1743 range.start,
1744 "mathstructure is only allowed in a module".to_string(),
1745 DiagnosticLevel::Error,
1746 );
1747 None
1748 }
1749
1750 #[inline]
1751 fn new_id(&mut self, prefix: Cow<'static, str>) -> Box<str> {
1752 self.name_counter.new_id(prefix)
1753 }
1754
1755 pub fn add_conservative_ext(
1756 &mut self,
1757 groups: &mut Groups<'a, '_, Pos, STeXToken<Pos>, Self>,
1758 orig: &SymbolReference<Pos>,
1759 range: StringRange<Pos>,
1760 ) -> Option<ModuleUri> {
1761 for g in groups.groups.iter_mut().rev() {
1762 match &mut g.kind {
1763 GroupKind::Module { uri, rules, .. } => {
1764 let name = self.new_id(Cow::Borrowed("EXTSTRUCT"));
1765 let euri = uri.clone() / &name.parse().ok()?;
1766 g.semantic_rules.push(SemanticRule::ConservativeExt(
1767 orig.clone(),
1768 ModuleRules::default(),
1769 ));
1770 rules.push(ModuleRule::ConservativeExt(
1771 orig.clone(),
1772 ModuleRules::default(),
1773 ));
1774 return Some(euri);
1775 }
1776 _ => (),
1777 }
1778 }
1779 groups.tokenizer.problem(
1780 range.start,
1781 "mathstructure is only allowed in a module".to_string(),
1782 DiagnosticLevel::Error,
1783 );
1784 None
1785 }
1786
1787 pub fn add_symbol(
1788 &mut self,
1789 groups: &mut Groups<'a, '_, Pos, STeXToken<Pos>, Self>,
1790 name: UriName,
1791 macroname: Option<std::sync::Arc<str>>,
1792 range: StringRange<Pos>,
1793 has_tp: bool,
1794 has_df: bool,
1795 argnum: u8,
1796 ) -> Option<SymbolReference<Pos>> {
1797 for g in groups.groups.iter_mut().rev() {
1798 match &mut g.kind {
1799 GroupKind::Module { uri, rules, .. }
1800 | GroupKind::MathStructure { uri, rules }
1801 | GroupKind::ConservativeExt(uri, rules) => {
1802 let suri = uri.clone() | name;
1803 let uri = SymbolReference {
1804 uri: suri,
1805 filepath: self.in_path.clone(),
1806 range,
1807 };
1808 for r in &*rules {
1809 match r {
1810 ModuleRule::Symbol(s) | ModuleRule::Structure { symbol: s, .. }
1811 if s.uri.uri == uri.uri =>
1812 {
1813 groups.tokenizer.problem(
1814 range.start,
1815 format!("symbol with name {} already exists", s.uri.uri),
1816 DiagnosticLevel::Warning,
1817 );
1818 }
1819 _ => (),
1820 }
1821 }
1822 self.module_store.add_verbalization(
1823 uri.uri.name.last(),
1824 &uri.uri,
1825 Language::English,
1826 );
1827 let rule = SymbolRule {
1828 uri,
1829 macroname,
1830 has_tp,
1831 has_df,
1832 argnum,
1833 };
1834 if MS::FULL
1835 && let Some((name, rule)) = rule.as_rule()
1836 {
1837 let old = groups.rules.insert(name.clone(), rule);
1838 if let Entry::Vacant(e) = g.inner.macro_rule_changes.entry(name) {
1839 e.insert(old);
1840 }
1841 }
1842 g.semantic_rules.push(SemanticRule::Symbol(rule.clone()));
1843 let uri = rule.uri.clone();
1844 rules.push(ModuleRule::Symbol(rule));
1845 return Some(uri);
1847 }
1848 _ => (),
1849 }
1850 }
1851 groups.tokenizer.problem(
1852 range.start,
1853 "\\symdecl is only allowed in a module".to_string(),
1854 DiagnosticLevel::Error,
1855 );
1856 None
1857 }
1858
1859 #[allow(clippy::case_sensitive_file_extension_comparisons)]
1860 #[allow(clippy::needless_pass_by_value)]
1861 #[allow(clippy::too_many_lines)]
1862 pub(super) fn resolve_module(
1863 &self,
1864 module: &'a str,
1865 archive: Option<ArchiveId>,
1866 ) -> Option<ModuleReference> {
1867 if let Some((m, _)) = self.find_module(module) {
1868 return Some(ModuleReference {
1869 uri: m.clone(),
1870 in_doc: self.doc_uri.clone(),
1871 rel_path: None,
1872 full_path: self.in_path.clone(),
1873 });
1874 }
1875 let (mut basepath, archive) = archive.as_ref().map_or_else(
1876 || {
1877 self.archive.and_then(|a| {
1878 self.in_path
1879 .as_ref()
1880 .and_then(|p| p.to_str())
1881 .and_then(|s| {
1882 s.find("source")
1883 .map(|i| (PathBuf::from(&s[..i - 1]).join("source"), a.clone()))
1884 })
1885 })
1886 },
1887 |a| {
1888 self.backend
1889 .with_local_archive(a, |a| a.map(|a| (a.source_dir(), a.uri().clone())))
1890 },
1891 )?;
1892
1893 let (mut path, module) = if let Some((a, b)) = module.split_once('?') {
1894 (a.trim(), b)
1895 } else {
1896 ("", module)
1897 };
1898
1899 let top_module = if let Some((t, _)) = module.split_once('/') {
1900 t
1901 } else {
1902 module
1903 };
1904
1905 let last = if let Some((p, last)) = path.rsplit_once('/') {
1906 basepath = p.split('/').fold(basepath, |p, s| p.join(s));
1907 last
1908 } else {
1909 path
1910 };
1911
1912 let uri: ModuleUri = if path.trim().is_empty() {
1913 PathUri::from(archive) | module.parse().ok()?
1914 } else {
1915 (PathUri::from(archive) / path.trim().parse::<UriPath>().ok()?) | module.parse().ok()?
1916 };
1917
1918 let p = basepath
1919 .join(last)
1920 .join(format!("{top_module}.{}.tex", self.language));
1921 if p.exists() {
1922 let rel_path = if path.is_empty() {
1923 format!("{top_module}.{}.tex", self.language)
1924 } else {
1925 format!("{path}/{top_module}.{}.tex", self.language)
1926 };
1927 return Some(ModuleReference {
1928 rel_path: Some(rel_path.into()),
1929 in_doc: uri.path_uri().clone() & (top_module.parse().ok()?, self.language),
1930 full_path: Some(p.into()),
1931 uri,
1932 });
1933 }
1934
1935 let p = basepath.join(last).join(format!("{top_module}.en.tex"));
1936 if p.exists() {
1937 let rel_path = if path.is_empty() {
1938 format!("{top_module}.en.tex")
1939 } else {
1940 format!("{path}/{top_module}.en.tex")
1941 };
1942 return Some(ModuleReference {
1943 rel_path: Some(rel_path.into()),
1944 in_doc: uri.path_uri().clone() & (top_module.parse().ok()?, Language::English),
1945 full_path: Some(p.into()),
1946 uri,
1947 });
1948 }
1949
1950 let p = basepath.join(last).join(format!("{top_module}.tex"));
1951 if p.exists() {
1952 let rel_path = if path.is_empty() {
1953 format!("{top_module}.tex")
1954 } else {
1955 format!("{path}/{top_module}.tex")
1956 };
1957 return Some(ModuleReference {
1958 rel_path: Some(rel_path.into()),
1959 in_doc: uri.path_uri().clone() & (top_module.parse().ok()?, Language::English),
1960 full_path: Some(p.into()),
1961 uri,
1962 });
1963 }
1964
1965 let path_uri = uri.path_uri().clone().up();
1966
1967 let p = basepath.join(format!("{last}.{}.tex", self.language));
1968 if p.exists() {
1969 return Some(ModuleReference {
1970 uri,
1971 in_doc: path_uri & (last.parse().ok()?, self.language),
1972 rel_path: Some(format!("{path}.{}.tex", self.language).into()),
1973 full_path: Some(p.into()),
1974 });
1975 }
1976
1977 let p = basepath.join(format!("{last}.en.tex"));
1978 if p.exists() {
1979 return Some(ModuleReference {
1980 uri,
1981 in_doc: path_uri & (last.parse().ok()?, Language::English),
1982 rel_path: Some(format!("{path}.en.tex").into()),
1983 full_path: Some(p.into()),
1984 });
1985 }
1986
1987 let p = basepath.join(format!("{last}.tex"));
1988 if p.exists() {
1989 return Some(ModuleReference {
1990 uri,
1991 in_doc: path_uri & (last.parse().ok()?, Language::English),
1992 rel_path: Some(format!("{path}.tex").into()),
1993 full_path: Some(p.into()),
1994 });
1995 }
1996 None
1997 }
1998
1999 fn find_module(&self, m: &str) -> Option<(&ModuleUri, &ModuleRules<Pos>)> {
2000 'top: for (muri, rls) in &self.modules {
2001 let mut f_steps = m.split('/');
2002 let mut m_steps = muri.module_name().steps();
2003 loop {
2004 let Some(f) = f_steps.next() else {
2005 if m_steps.next().is_none() {
2006 return Some((muri, rls));
2007 }
2008 continue 'top;
2009 };
2010 let Some(m) = m_steps.next() else {
2011 continue 'top;
2012 };
2013 if f != m {
2014 continue 'top;
2015 }
2016 }
2017 }
2018 None
2019 }
2020}
2021
2022#[derive(Default)]
2023#[allow(clippy::large_enum_variant)]
2024pub enum GroupKind<Pos: StringPosition> {
2025 #[default]
2026 None,
2027 Problem,
2028 Module {
2029 uri: ModuleUri,
2030 rules: Vec<ModuleRule<Pos>>,
2031 },
2032 MathStructure {
2033 uri: ModuleUri,
2034 rules: Vec<ModuleRule<Pos>>,
2035 },
2036 ConservativeExt(ModuleUri, Vec<ModuleRule<Pos>>),
2037 DefPara(Vec<SymbolReference<Pos>>),
2038 Morphism {
2039 domain: ModuleOrStruct<Pos>,
2040 rules: Vec<ModuleRules<Pos>>,
2041 specs: VecMap<SymbolReference<Pos>, MorphismSpec<Pos>>,
2042 },
2043}
2044
2045#[derive(Clone, Debug, Default)]
2046pub struct MorphismSpec<Pos: StringPosition> {
2047 pub macroname: Option<Box<str>>,
2048 pub new_name: Option<UriName>,
2049 pub is_assigned_at: Option<StringRange<Pos>>,
2050 pub decl_range: StringRange<Pos>,
2051}
2052
2053#[derive(Debug, Clone, serde::Serialize)]
2054pub enum ModuleOrStruct<Pos: StringPosition> {
2055 Module(ModuleReference),
2056 Struct(SymbolReference<Pos>),
2057}
2058
2059#[non_exhaustive]
2060pub struct STeXGroup<'a, MS: STeXModuleStore, Pos: StringPosition + 'a> {
2061 pub inner: Group<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>>,
2062 pub kind: GroupKind<Pos>,
2063 pub semantic_rules: Vec<SemanticRule<Pos>>,
2064 pub uses: VecSet<ModuleUri>,
2065}
2066
2067pub enum SemanticRule<Pos: StringPosition> {
2068 Symbol(SymbolRule<Pos>),
2069 Module(ModuleReference, ModuleRules<Pos>),
2070 Structure {
2071 symbol: SymbolRule<Pos>,
2072 rules: ModuleRules<Pos>,
2074 },
2075 ConservativeExt(SymbolReference<Pos>, ModuleRules<Pos>),
2076 StructureImport(SymbolReference<Pos>, ModuleRules<Pos>),
2077}
2078
2079impl<'a, MS: STeXModuleStore, Pos: StringPosition>
2080 GroupState<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>> for STeXGroup<'a, MS, Pos>
2081{
2082 #[inline]
2083 fn new(parent: Option<&mut Self>) -> Self {
2084 Self {
2085 inner: Group::new(parent.map(|p| &mut p.inner)),
2086 kind: GroupKind::None,
2087 semantic_rules: Vec::new(),
2088 uses: VecSet::default(),
2089 }
2090 }
2091
2092 #[inline]
2093 fn inner(&self) -> &Group<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>> {
2094 &self.inner
2095 }
2096 #[inline]
2097 fn inner_mut(&mut self) -> &mut Group<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>> {
2098 &mut self.inner
2099 }
2100 #[inline]
2101 fn close(self, parser: &mut LaTeXParser<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>>) {
2102 self.inner.close(parser);
2103 }
2104 #[inline]
2105 fn add_macro_rule(
2106 &mut self,
2107 name: Cow<'a, str>,
2108 old: Option<AnyMacro<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>>>,
2109 ) {
2110 self.inner.add_macro_rule(name, old);
2111 }
2112 #[inline]
2113 fn add_environment_rule(
2114 &mut self,
2115 name: Cow<'a, str>,
2116 old: Option<AnyEnv<'a, Pos, STeXToken<Pos>, STeXParseState<'a, Pos, MS>>>,
2117 ) {
2118 self.inner.add_environment_rule(name, old);
2119 }
2120 #[inline]
2121 fn letter_change(&mut self, old: &str) {
2122 self.inner.letter_change(old);
2123 }
2124}
2125
2126#[derive(Clone, Debug)]
2127pub enum MacroArg<Pos: StringPosition> {
2128 Symbol(SymbolReference<Pos>, u8),
2129 Variable(UriName, StringRange<Pos>, bool, u8),
2130}
2131
2132impl<'a, MS: STeXModuleStore, Pos: StringPosition> ParserState<'a, Pos, STeXToken<Pos>>
2133 for STeXParseState<'a, Pos, MS>
2134{
2135 type Group = STeXGroup<'a, MS, Pos>;
2136 type MacroArg = MacroArg<Pos>;
2137 #[inline]
2138 fn from_text(
2139 &self,
2140 r: StringRange<Pos>,
2141 text: &'a str,
2142 in_document: bool,
2143 in_math: bool,
2144 groups: &mut Groups<'a, '_, Pos, STeXToken<Pos>, Self>,
2145 ) -> Option<STeXToken<Pos>> {
2146 if MS::FULL && in_document && !in_math {
2147 let f = |s: &SymbolUri| {
2148 for g in groups.groups.iter().rev() {
2149 for r in g.semantic_rules.iter().rev() {
2150 if let SemanticRule::Symbol(sym)
2151 | SemanticRule::Structure { symbol: sym, .. } = r
2152 && sym.uri.uri == *s
2153 {
2154 return false;
2155 }
2156 }
2157 }
2158 true
2159 };
2160 let r = self.module_store.add_text(r, text, self.language, &f);
2161 if let Some(STeXToken::Vec(v)) = &r {
2162 for t in v {
2163 if let STeXToken::SnifySuggestion { range, .. } = t {
2164 (groups.tokenizer.err)(
2165 "snify suggestion".to_string(),
2166 *range,
2167 DiagnosticLevel::Info,
2168 )
2169 }
2170 }
2171 }
2172 r
2173 } else {
2174 None
2175 }
2176 }
2177}