Skip to main content

meta_language/
link_network.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt;
3use std::sync::Arc;
4
5use crate::configuration::{ParseConfiguration, TriviaAttachmentPolicy};
6use crate::embedded_region_parser;
7use crate::language_parser::{BuiltInLanguageParser, LanguageParser};
8use crate::language_profile::LanguageProfile;
9use crate::link_flags::LinkFlags;
10use crate::mixed_regions::EmbeddedRegion;
11use crate::natural_language::annotate_natural_language;
12use crate::query::{LinkQuery, QueryMatch, QueryPredicateHost, RejectPredicateHost};
13use crate::self_description::{definition_expression, SELF_DESCRIPTION_ROOTS};
14use crate::source::{ByteRange, Point, SourceSpan};
15use crate::substitution::{
16    SubstitutionBindings, SubstitutionReport, SubstitutionRule, VariableSubstitutionRule,
17};
18use crate::verification::{VerificationIssue, VerificationIssueKind, VerificationReport};
19
20/// Stable identifier for a link inside a [`LinkNetwork`].
21#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct LinkId(pub(crate) u64);
23
24impl LinkId {
25    /// Builds a link identifier from its numeric representation.
26    #[must_use]
27    pub const fn from_u64(value: u64) -> Self {
28        Self(value)
29    }
30
31    /// Returns the numeric identifier.
32    #[must_use]
33    pub const fn as_u64(self) -> u64 {
34        self.0
35    }
36}
37
38impl fmt::Display for LinkId {
39    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(formatter, "{}", self.0)
41    }
42}
43
44/// Coarse role for a link in the meta-language network.
45#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
46pub enum LinkType {
47    Link,
48    Reference,
49    Relation,
50    Language,
51    Grammar,
52    Type,
53    Concept,
54    Syntax,
55    Field,
56    Trivia,
57    Token,
58    Document,
59    Semantic,
60    Region,
61    Object,
62}
63
64/// View of a links network with lower-level data optionally stripped away.
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66pub enum NetworkProjection {
67    /// Full lossless network, including all source-preservation links.
68    Lossless,
69    /// Concrete syntax view, including tokens, trivia, fields, and spans.
70    ConcreteSyntax,
71    /// Abstract syntax view, excluding lossless token and trivia links.
72    AbstractSyntax,
73    /// Meaning-focused view, keeping semantic, concept, type, and language links.
74    Semantic,
75}
76
77impl NetworkProjection {
78    /// Human-readable projection name.
79    #[must_use]
80    pub const fn label(self) -> &'static str {
81        match self {
82            Self::Lossless => "lossless",
83            Self::ConcreteSyntax => "concrete syntax",
84            Self::AbstractSyntax => "abstract syntax",
85            Self::Semantic => "semantic",
86        }
87    }
88
89    fn includes(self, link: &Link) -> bool {
90        match self {
91            Self::Lossless => true,
92            Self::ConcreteSyntax => link.metadata().link_type() != Some(LinkType::Semantic),
93            Self::AbstractSyntax => !matches!(
94                link.metadata().link_type(),
95                Some(LinkType::Token | LinkType::Trivia)
96            ),
97            Self::Semantic => matches!(
98                link.metadata().link_type(),
99                Some(LinkType::Semantic | LinkType::Concept | LinkType::Type | LinkType::Language)
100            ),
101        }
102    }
103}
104
105impl fmt::Display for LinkType {
106    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
107        let name = match self {
108            Self::Link => "link",
109            Self::Reference => "reference",
110            Self::Relation => "relation",
111            Self::Language => "language",
112            Self::Grammar => "grammar",
113            Self::Type => "type",
114            Self::Concept => "concept",
115            Self::Syntax => "syntax",
116            Self::Field => "field",
117            Self::Trivia => "trivia",
118            Self::Token => "token",
119            Self::Document => "document",
120            Self::Semantic => "semantic",
121            Self::Region => "region",
122            Self::Object => "object",
123        };
124        formatter.write_str(name)
125    }
126}
127
128/// Metadata carried by a link.
129#[derive(Clone, Debug, Default, PartialEq, Eq)]
130pub struct LinkMetadata {
131    link_type: Option<LinkType>,
132    named: bool,
133    term: Option<Arc<str>>,
134    definition: Option<Arc<str>>,
135    language: Option<Arc<str>>,
136    span: Option<SourceSpan>,
137    flags: LinkFlags,
138}
139
140impl LinkMetadata {
141    /// Creates empty metadata.
142    #[must_use]
143    pub fn new() -> Self {
144        Self::default()
145    }
146
147    /// Returns metadata with a link type.
148    #[must_use]
149    pub const fn with_link_type(mut self, link_type: LinkType) -> Self {
150        self.link_type = Some(link_type);
151        self
152    }
153
154    /// Returns metadata with the named flag set.
155    #[must_use]
156    pub const fn with_named(mut self, named: bool) -> Self {
157        self.named = named;
158        self
159    }
160
161    /// Returns metadata with a term label.
162    #[must_use]
163    pub fn with_term(mut self, term: impl Into<String>) -> Self {
164        self.term = Some(Arc::from(term.into()));
165        self
166    }
167
168    /// Returns metadata with a self-description definition.
169    #[must_use]
170    pub fn with_definition(mut self, definition: impl Into<String>) -> Self {
171        self.definition = Some(Arc::from(definition.into()));
172        self
173    }
174
175    /// Returns metadata with a language label.
176    #[must_use]
177    pub fn with_language(mut self, language: impl Into<String>) -> Self {
178        self.language = Some(Arc::from(language.into()));
179        self
180    }
181
182    /// Returns metadata with a source span.
183    #[must_use]
184    pub const fn with_span(mut self, span: SourceSpan) -> Self {
185        self.span = Some(span);
186        self
187    }
188
189    /// Returns metadata with parse status flags.
190    #[must_use]
191    pub const fn with_flags(mut self, flags: LinkFlags) -> Self {
192        self.flags = flags;
193        self
194    }
195
196    /// Link type, when known.
197    #[must_use]
198    pub const fn link_type(&self) -> Option<LinkType> {
199        self.link_type
200    }
201
202    /// Whether this link is named.
203    #[must_use]
204    pub const fn is_named(&self) -> bool {
205        self.named
206    }
207
208    /// Term label attached to this link.
209    #[must_use]
210    pub fn term(&self) -> Option<&str> {
211        self.term.as_deref()
212    }
213
214    /// Self-description definition attached to this link.
215    #[must_use]
216    pub fn definition(&self) -> Option<&str> {
217        self.definition.as_deref()
218    }
219
220    /// Language label attached to this link.
221    #[must_use]
222    pub fn language(&self) -> Option<&str> {
223        self.language.as_deref()
224    }
225
226    /// Source span attached to this link.
227    #[must_use]
228    pub const fn span(&self) -> Option<SourceSpan> {
229        self.span
230    }
231
232    /// Parse status flags attached to this link.
233    #[must_use]
234    pub const fn flags(&self) -> LinkFlags {
235        self.flags
236    }
237}
238
239/// A link is an n-tuple of references to other links.
240#[derive(Clone, Debug, PartialEq, Eq)]
241pub struct Link {
242    pub(crate) id: LinkId,
243    pub(crate) references: Arc<[LinkId]>,
244    pub(crate) metadata: LinkMetadata,
245}
246
247impl Link {
248    /// Link identifier.
249    #[must_use]
250    pub const fn id(&self) -> LinkId {
251        self.id
252    }
253
254    /// Ordered references carried by this link.
255    #[must_use]
256    pub fn references(&self) -> &[LinkId] {
257        &self.references
258    }
259
260    /// Metadata carried by this link.
261    #[must_use]
262    pub const fn metadata(&self) -> &LinkMetadata {
263        &self.metadata
264    }
265
266    const fn metadata_mut(&mut self) -> &mut LinkMetadata {
267        &mut self.metadata
268    }
269}
270
271/// Mutable links network for CST, AST, semantic, and self-description links.
272#[derive(Clone, Debug, Default, PartialEq, Eq)]
273pub struct LinkNetwork {
274    pub(crate) next_id: u64,
275    pub(crate) links: BTreeMap<LinkId, Arc<Link>>,
276    pub(crate) terms: BTreeMap<Arc<str>, LinkId>,
277    pub(crate) concept_syntax: BTreeMap<(Arc<str>, Arc<str>), Arc<str>>,
278    pub(crate) strings: BTreeSet<Arc<str>>,
279}
280
281impl LinkNetwork {
282    /// Creates an empty links network.
283    #[must_use]
284    pub const fn new() -> Self {
285        Self {
286            next_id: 1,
287            links: BTreeMap::new(),
288            terms: BTreeMap::new(),
289            concept_syntax: BTreeMap::new(),
290            strings: BTreeSet::new(),
291        }
292    }
293
294    /// Creates a links network containing the common self-description roots.
295    #[must_use]
296    pub fn self_describing() -> Self {
297        let mut network = Self::new();
298        for root in SELF_DESCRIPTION_ROOTS {
299            let definition = definition_expression(root.term, root.references);
300            network.insert_typed_point(root.term, root.link_type, Some(&definition));
301        }
302
303        for root in SELF_DESCRIPTION_ROOTS {
304            let mut references = Vec::with_capacity(root.references.len() + 1);
305            references.push(
306                network
307                    .find_term(root.term)
308                    .expect("seeded self-description root exists"),
309            );
310            for reference in root.references {
311                references.push(
312                    network
313                        .find_term(reference)
314                        .expect("seeded self-description reference exists"),
315                );
316            }
317
318            network.insert_dynamic_link(
319                &references,
320                LinkMetadata::new().with_link_type(LinkType::Relation),
321            );
322        }
323
324        network
325    }
326
327    /// Serializes the seeded self-description roots as LiNo-style definition lines.
328    #[must_use]
329    pub fn self_description_text(&self) -> String {
330        let mut output = String::new();
331        for root in SELF_DESCRIPTION_ROOTS {
332            let Some(id) = self.find_term(root.term) else {
333                continue;
334            };
335            let Some(definition) = self.definition_for(id) else {
336                continue;
337            };
338            output.push_str(definition);
339            output.push('\n');
340        }
341        output
342    }
343
344    /// Parses plain source text into a lossless token network.
345    ///
346    /// This is the default parse operation. It is lossless by construction; use
347    /// [`LinkNetwork::projected_links`] when a lower-level view should be
348    /// stripped away for CST, AST, or semantic-only work.
349    #[must_use]
350    pub fn parse(text: &str, language: &str, configuration: ParseConfiguration) -> Self {
351        let mut network = BuiltInLanguageParser.parse_source(text, language, configuration);
352        if let Some(profile) = configuration.profile().and_then(LanguageProfile::builtin) {
353            profile.declare_in(&mut network);
354        }
355        network
356    }
357
358    /// Parses plain source text into a lossless token network.
359    ///
360    /// This parser boundary preserves source spans, trivia links, recovery
361    /// markers, and mixed-region metadata behind the same network
362    /// representation.
363    #[must_use]
364    pub fn parse_lossless_text(
365        text: &str,
366        language: &str,
367        configuration: ParseConfiguration,
368    ) -> Self {
369        let (mut network, document) = Self::new_parse_document(text, language);
370
371        let mut row = 0;
372        let mut column = 0;
373        let mut open_parentheses = Vec::new();
374        for (start, character) in text.char_indices() {
375            let start_point = Point::new(row, column);
376            let end = start + character.len_utf8();
377            if character == '\n' {
378                row += 1;
379                column = 0;
380            } else {
381                column += 1;
382            }
383            let end_point = Point::new(row, column);
384            let span = SourceSpan::new(ByteRange::new(start, end), start_point, end_point);
385            let mut metadata = LinkMetadata::new()
386                .with_link_type(LinkType::Token)
387                .with_named(!character.is_whitespace())
388                .with_term(character.to_string())
389                .with_language(language)
390                .with_span(span);
391
392            if character.is_whitespace() {
393                metadata = metadata.with_flags(LinkFlags::extra());
394            }
395
396            let token = network.insert_link([document], metadata);
397            match character {
398                '(' => open_parentheses.push(token),
399                ')' if open_parentheses.pop().is_none() => {
400                    network.set_flags(token, LinkFlags::error());
401                }
402                _ => {}
403            }
404            if character.is_whitespace() {
405                network.attach_trivia(
406                    document,
407                    token,
408                    span,
409                    configuration.trivia_attachment_policy(),
410                );
411            }
412        }
413
414        let missing_span = SourceSpan::new(
415            ByteRange::new(text.len(), text.len()),
416            end_point_for_text(text),
417            end_point_for_text(text),
418        );
419        for open_parenthesis in open_parentheses {
420            network.set_flags(open_parenthesis, LinkFlags::containing_error());
421            network.insert_link(
422                [document],
423                LinkMetadata::new()
424                    .with_link_type(LinkType::Token)
425                    .with_named(false)
426                    .with_term(")")
427                    .with_language(language)
428                    .with_span(missing_span)
429                    .with_flags(LinkFlags::missing()),
430            );
431        }
432
433        network.attach_embedded_regions(document, text, language, configuration);
434        annotate_natural_language(&mut network, document, text, language, configuration);
435
436        network
437    }
438
439    pub(crate) fn new_parse_document(text: &str, language: &str) -> (Self, LinkId) {
440        let mut network = Self::self_describing();
441        let language_link = network.insert_typed_point(language, LinkType::Language, None);
442        let document_span = SourceSpan::new(
443            ByteRange::new(0, text.len()),
444            Point::new(0, 0),
445            end_point_for_text(text),
446        );
447        let document = network.insert_link(
448            [language_link],
449            LinkMetadata::new()
450                .with_link_type(LinkType::Document)
451                .with_named(true)
452                .with_term(format!("{language} document"))
453                .with_language(language)
454                .with_span(document_span),
455        );
456        (network, document)
457    }
458
459    /// Number of links in the network.
460    #[must_use]
461    pub fn len(&self) -> usize {
462        self.links.len()
463    }
464
465    /// Whether the network contains no links.
466    #[must_use]
467    pub fn is_empty(&self) -> bool {
468        self.links.is_empty()
469    }
470
471    /// Number of network handles sharing one immutable link allocation.
472    #[must_use]
473    pub fn shared_link_count(&self, id: LinkId) -> Option<usize> {
474        self.links.get(&id).map(Arc::strong_count)
475    }
476
477    /// Number of internal handles sharing an interned string value.
478    #[must_use]
479    pub fn interned_string_count(&self, value: &str) -> Option<usize> {
480        self.strings.get(value).map(Arc::strong_count)
481    }
482
483    /// Iterates over links in identifier order.
484    pub fn links(&self) -> impl Iterator<Item = &Link> {
485        self.links.values().map(Arc::as_ref)
486    }
487
488    /// Iterates over links included in the selected projection.
489    pub fn projected_links(&self, projection: NetworkProjection) -> impl Iterator<Item = &Link> {
490        self.links().filter(move |link| projection.includes(link))
491    }
492
493    /// Reconstructs source text from non-missing token links ordered by span.
494    #[must_use]
495    pub fn reconstruct_text(&self) -> String {
496        let mut tokens = self
497            .links()
498            .filter(|link| link.metadata().link_type() == Some(LinkType::Token))
499            .filter(|link| !link.metadata().flags().is_missing())
500            .filter_map(|link| {
501                Some((
502                    link.metadata().span()?.byte_range(),
503                    link.id().as_u64(),
504                    link.metadata().term()?.to_string(),
505                ))
506            })
507            .collect::<Vec<_>>();
508
509        tokens.sort_by_key(|(range, id, _term)| (range.start(), *id));
510        let mut reconstructed = String::new();
511        let mut covered_until = 0;
512        for (range, _id, term) in tokens {
513            if range.start() < covered_until {
514                continue;
515            }
516            reconstructed.push_str(&term);
517            covered_until = range.end();
518        }
519        reconstructed
520    }
521
522    /// Returns embedded mixed-language regions discovered during parse.
523    #[must_use]
524    pub fn embedded_regions(&self) -> Vec<EmbeddedRegion> {
525        self.links()
526            .filter(|link| link.metadata().link_type() == Some(LinkType::Region))
527            .filter_map(|link| {
528                Some(EmbeddedRegion::new(
529                    link.metadata().language()?.to_string(),
530                    link.metadata().span()?,
531                ))
532            })
533            .collect()
534    }
535
536    /// Returns links matching a structural query.
537    #[must_use]
538    pub fn query_links(&self, query: &LinkQuery) -> Vec<&Link> {
539        self.query_matches(query)
540            .into_iter()
541            .filter_map(|query_match| self.link(query_match.link_id()))
542            .collect()
543    }
544
545    /// Returns query matches with capture bindings.
546    #[must_use]
547    pub fn query_matches(&self, query: &LinkQuery) -> Vec<QueryMatch> {
548        self.query_matches_with(query, &RejectPredicateHost)
549    }
550
551    /// Returns query matches with host-evaluated predicate support.
552    #[must_use]
553    pub fn query_matches_with(
554        &self,
555        query: &LinkQuery,
556        predicate_host: &impl QueryPredicateHost,
557    ) -> Vec<QueryMatch> {
558        self.links()
559            .flat_map(|link| query.matches_in_network(self, link, predicate_host))
560            .collect()
561    }
562
563    /// Inserts a self-referential point link for a term.
564    pub fn insert_point(&mut self, term: &str) -> LinkId {
565        self.insert_typed_point(term, LinkType::Concept, None)
566    }
567
568    /// Inserts an object-identity point link.
569    pub fn insert_object(&mut self, term: &str) -> LinkId {
570        self.insert_typed_point(term, LinkType::Object, None)
571    }
572
573    /// Inserts a relation link with source span metadata.
574    pub fn insert_relation<const N: usize>(
575        &mut self,
576        references: [LinkId; N],
577        link_type: LinkType,
578        span: SourceSpan,
579    ) -> LinkId {
580        self.insert_link(
581            references,
582            LinkMetadata::new()
583                .with_link_type(link_type)
584                .with_span(span),
585        )
586    }
587
588    /// Inserts a labeled field relation as a regular link.
589    pub fn insert_field(&mut self, parent: LinkId, label: &str, child: LinkId) -> LinkId {
590        let label_link = self.insert_typed_point(
591            label,
592            LinkType::Field,
593            Some("A field label names a relation between links."),
594        );
595        self.insert_link(
596            [parent, label_link, child],
597            LinkMetadata::new().with_link_type(LinkType::Field),
598        )
599    }
600
601    /// Inserts a link from references and metadata.
602    pub fn insert_link<const N: usize>(
603        &mut self,
604        references: [LinkId; N],
605        metadata: LinkMetadata,
606    ) -> LinkId {
607        let id = self.allocate_id();
608        let metadata = self.intern_metadata(metadata);
609        self.links.insert(
610            id,
611            Arc::new(Link {
612                id,
613                references: Arc::from(references.to_vec()),
614                metadata,
615            }),
616        );
617        id
618    }
619
620    /// Reconstructs a concept using a target language syntax mapping.
621    #[must_use]
622    pub fn reconstruct_concept(&self, concept: &str, language: &str) -> Option<&str> {
623        let key = (Arc::<str>::from(concept), Arc::<str>::from(language));
624        self.concept_syntax.get(&key).map(Arc::as_ref)
625    }
626
627    /// Applies a match-and-substitute rule over exact reference lists.
628    pub fn apply_substitution(&mut self, rule: &SubstitutionRule) -> SubstitutionReport {
629        let mut report = SubstitutionReport::default();
630
631        if rule.pattern().is_empty() {
632            if !rule.replacement().is_empty() {
633                let created = self.insert_dynamic_link(
634                    rule.replacement(),
635                    LinkMetadata::new().with_link_type(LinkType::Relation),
636                );
637                report.created.push(created);
638            }
639            return report;
640        }
641
642        let matched = self
643            .links()
644            .filter(|link| link.references() == rule.pattern())
645            .map(Link::id)
646            .collect::<Vec<_>>();
647
648        if rule.replacement().is_empty() {
649            for id in matched {
650                if self.links.remove(&id).is_some() {
651                    report.deleted.push(id);
652                }
653            }
654            return report;
655        }
656
657        for id in matched {
658            if let Some(link) = self.links.get_mut(&id) {
659                Arc::make_mut(link).references = Arc::from(rule.replacement().to_vec());
660                report.updated.push(id);
661            }
662        }
663
664        report
665    }
666
667    /// Applies a match-and-substitute rule with link-cli-style variables.
668    pub fn apply_variable_substitution(
669        &mut self,
670        rule: &VariableSubstitutionRule,
671    ) -> SubstitutionReport {
672        let mut report = SubstitutionReport::default();
673
674        if rule.pattern().is_empty() {
675            let bindings = SubstitutionBindings::default();
676            if let Some(references) = bindings.resolve_values(rule.replacement()) {
677                if !references.is_empty() {
678                    let created = self.insert_dynamic_link(
679                        &references,
680                        LinkMetadata::new().with_link_type(LinkType::Relation),
681                    );
682                    report.created.push(created);
683                    report.bindings.push(bindings);
684                }
685            }
686            return report;
687        }
688
689        let matched = self
690            .links()
691            .filter_map(|link| rule.match_link(link).map(|bindings| (link.id(), bindings)))
692            .collect::<Vec<_>>();
693
694        if rule.replacement().is_empty() {
695            for (id, bindings) in matched {
696                if self.links.remove(&id).is_some() {
697                    report.deleted.push(id);
698                    report.bindings.push(bindings);
699                }
700            }
701            return report;
702        }
703
704        for (id, bindings) in matched {
705            let Some(references) = bindings.resolve_values(rule.replacement()) else {
706                continue;
707            };
708            if let Some(link) = self.links.get_mut(&id) {
709                Arc::make_mut(link).references = Arc::from(references);
710                report.updated.push(id);
711                report.bindings.push(bindings);
712            }
713        }
714
715        report
716    }
717
718    /// Returns a link by id.
719    #[must_use]
720    pub fn link(&self, id: LinkId) -> Option<&Link> {
721        self.links.get(&id).map(Arc::as_ref)
722    }
723
724    /// Finds a self-description or named term link.
725    #[must_use]
726    pub fn find_term(&self, term: &str) -> Option<LinkId> {
727        self.terms.get(term).copied()
728    }
729
730    /// Finds the definition attached to a term link.
731    #[must_use]
732    pub fn definition_for(&self, id: LinkId) -> Option<&str> {
733        self.link(id).and_then(|link| link.metadata().definition())
734    }
735
736    /// Sets a source span on an existing link.
737    pub fn set_span(&mut self, id: LinkId, span: SourceSpan) -> bool {
738        let Some(link) = self.links.get_mut(&id) else {
739            return false;
740        };
741        Arc::make_mut(link).metadata_mut().span = Some(span);
742        true
743    }
744
745    /// Sets parse flags on an existing link.
746    pub fn set_flags(&mut self, id: LinkId, flags: LinkFlags) -> bool {
747        let Some(link) = self.links.get_mut(&id) else {
748            return false;
749        };
750        Arc::make_mut(link).metadata_mut().flags = flags;
751        true
752    }
753
754    pub(crate) fn set_term(&mut self, id: LinkId, term: impl Into<String>) -> bool {
755        let term = self.intern_arc(Arc::from(term.into()));
756        let Some(link) = self.links.get_mut(&id) else {
757            return false;
758        };
759        Arc::make_mut(link).metadata_mut().term = Some(term);
760        true
761    }
762
763    /// Verifies that the selected region has no error or missing links.
764    #[must_use]
765    pub fn verify_full_match(&self, region: Option<ByteRange>) -> VerificationReport {
766        let issues = self
767            .links()
768            .filter(|link| link_is_in_region(link, region))
769            .filter_map(|link| {
770                let flags = link.metadata().flags();
771                let kind = if flags.is_error() {
772                    VerificationIssueKind::ErrorLink
773                } else if flags.is_missing() {
774                    VerificationIssueKind::MissingLink
775                } else if flags.has_error() {
776                    VerificationIssueKind::HasErrorLink
777                } else {
778                    return None;
779                };
780
781                Some(VerificationIssue::new(
782                    link.id(),
783                    kind,
784                    link.metadata().span(),
785                ))
786            })
787            .collect();
788        VerificationReport::new(issues)
789    }
790
791    pub(crate) fn insert_typed_point(
792        &mut self,
793        term: &str,
794        link_type: LinkType,
795        definition: Option<&str>,
796    ) -> LinkId {
797        let definition = definition.map(|definition| self.intern_arc(Arc::from(definition)));
798        if let Some(id) = self.terms.get(term).copied() {
799            if let Some(definition) = definition {
800                if let Some(link) = self.links.get_mut(&id) {
801                    Arc::make_mut(link).metadata_mut().definition = Some(definition);
802                }
803            }
804            return id;
805        }
806
807        let id = self.allocate_id();
808        let term = self.intern_arc(Arc::from(term));
809        let mut metadata = LinkMetadata::new()
810            .with_link_type(link_type)
811            .with_named(true);
812        metadata.term = Some(Arc::clone(&term));
813        if let Some(definition) = definition {
814            metadata.definition = Some(definition);
815        }
816        self.links.insert(
817            id,
818            Arc::new(Link {
819                id,
820                references: Arc::from(vec![id]),
821                metadata,
822            }),
823        );
824        self.terms.insert(term, id);
825        id
826    }
827
828    pub(crate) fn cache_concept_syntax(
829        &mut self,
830        concept: &str,
831        language: &str,
832        syntax: &str,
833        update_reconstruction: bool,
834    ) {
835        let concept = self.intern_arc(Arc::from(concept));
836        let language = self.intern_arc(Arc::from(language));
837        let syntax = self.intern_arc(Arc::from(syntax));
838
839        if update_reconstruction
840            || !self
841                .concept_syntax
842                .contains_key(&(Arc::clone(&concept), Arc::clone(&language)))
843        {
844            self.concept_syntax.insert((concept, language), syntax);
845        }
846    }
847
848    pub(crate) fn attach_trivia(
849        &mut self,
850        document: LinkId,
851        token: LinkId,
852        span: SourceSpan,
853        policy: TriviaAttachmentPolicy,
854    ) {
855        match policy {
856            TriviaAttachmentPolicy::ContainmentLink => {
857                self.insert_containment_trivia(document, token, span);
858            }
859            TriviaAttachmentPolicy::TokenLink => {
860                self.insert_token_trivia(token, span);
861            }
862            TriviaAttachmentPolicy::Both => {
863                self.insert_containment_trivia(document, token, span);
864                self.insert_token_trivia(token, span);
865            }
866        }
867    }
868
869    fn insert_containment_trivia(&mut self, document: LinkId, token: LinkId, span: SourceSpan) {
870        self.insert_link(
871            [document, token],
872            LinkMetadata::new()
873                .with_link_type(LinkType::Trivia)
874                .with_term("containment trivia")
875                .with_span(span)
876                .with_flags(LinkFlags::extra()),
877        );
878    }
879
880    fn insert_token_trivia(&mut self, token: LinkId, span: SourceSpan) {
881        self.insert_link(
882            [token],
883            LinkMetadata::new()
884                .with_link_type(LinkType::Trivia)
885                .with_term("token trivia")
886                .with_span(span)
887                .with_flags(LinkFlags::extra()),
888        );
889    }
890
891    pub(crate) fn insert_dynamic_link(
892        &mut self,
893        references: &[LinkId],
894        metadata: LinkMetadata,
895    ) -> LinkId {
896        let id = self.allocate_id();
897        let metadata = self.intern_metadata(metadata);
898        let term = metadata.term.clone();
899        self.links.insert(
900            id,
901            Arc::new(Link {
902                id,
903                references: Arc::from(references.to_vec()),
904                metadata,
905            }),
906        );
907        if let Some(term) = term {
908            self.terms.insert(term, id);
909        }
910        id
911    }
912
913    pub(crate) fn set_references(&mut self, id: LinkId, references: &[LinkId]) -> bool {
914        let Some(link) = self.links.get_mut(&id) else {
915            return false;
916        };
917        Arc::make_mut(link).references = Arc::from(references.to_vec());
918        true
919    }
920
921    pub(crate) fn attach_embedded_regions(
922        &mut self,
923        document: LinkId,
924        text: &str,
925        language: &str,
926        configuration: ParseConfiguration,
927    ) {
928        embedded_region_parser::attach_embedded_regions(
929            self,
930            document,
931            text,
932            language,
933            configuration,
934        );
935    }
936
937    fn intern_metadata(&mut self, mut metadata: LinkMetadata) -> LinkMetadata {
938        metadata.term = metadata.term.map(|value| self.intern_arc(value));
939        metadata.definition = metadata.definition.map(|value| self.intern_arc(value));
940        metadata.language = metadata.language.map(|value| self.intern_arc(value));
941        metadata
942    }
943
944    fn intern_arc(&mut self, value: Arc<str>) -> Arc<str> {
945        if let Some(interned) = self.strings.get(value.as_ref()) {
946            return Arc::clone(interned);
947        }
948
949        self.strings.insert(Arc::clone(&value));
950        value
951    }
952
953    const fn allocate_id(&mut self) -> LinkId {
954        let id = LinkId(self.next_id);
955        self.next_id += 1;
956        id
957    }
958}
959
960fn link_is_in_region(link: &Link, region: Option<ByteRange>) -> bool {
961    let Some(region) = region else {
962        return true;
963    };
964    link.metadata()
965        .span()
966        .is_some_and(|span| span.byte_range().intersects(region))
967}
968
969fn end_point_for_text(text: &str) -> Point {
970    let mut row = 0;
971    let mut column = 0;
972    for character in text.chars() {
973        if character == '\n' {
974            row += 1;
975            column = 0;
976        } else {
977            column += 1;
978        }
979    }
980    Point::new(row, column)
981}