lang_turtle/lang/
context.rs

1use std::{
2    collections::{HashMap, HashSet},
3    ops::Index,
4};
5
6use bevy_ecs::component::Component;
7use lsp_core::{prelude::Token, util::Spanned};
8use similar::ChangeTag;
9
10pub struct TokenIdx<'a> {
11    pub tokens: &'a Vec<Spanned<Token>>,
12}
13impl<'a> Index<usize> for TokenIdx<'a> {
14    type Output = Token;
15
16    fn index(&self, index: usize) -> &Self::Output {
17        &self.tokens[index].value()
18    }
19}
20#[derive(Clone, Copy)]
21pub struct Ctx<'a> {
22    context: &'a Context,
23}
24
25impl<'a> Ctx<'a> {
26    pub fn find_was(&self, idx: usize) -> Option<ContextKind> {
27        if let Some(idx) = self.context.current_to_prev.get(&idx) {
28            if self.context.subjects.contains(idx) {
29                return Some(ContextKind::Subject);
30            }
31
32            if self.context.predicates.contains(idx) {
33                return Some(ContextKind::Predicate);
34            }
35
36            if self.context.objects.contains(idx) {
37                return Some(ContextKind::Object);
38            }
39        }
40        None
41    }
42    pub fn was(&self, idx: usize, kind: ContextKind) -> bool {
43        match kind {
44            ContextKind::Subject => self.was_subject(idx),
45            ContextKind::Predicate => self.was_predicate(idx),
46            ContextKind::Object => self.was_object(idx),
47        }
48    }
49
50    pub fn was_subject(&self, idx: usize) -> bool {
51        self.context
52            .current_to_prev
53            .get(&idx)
54            .map(|old| self.context.subjects.contains(old))
55            .unwrap_or_default()
56    }
57
58    pub fn was_object(&self, idx: usize) -> bool {
59        self.context
60            .current_to_prev
61            .get(&idx)
62            .map(|old| self.context.objects.contains(old))
63            .unwrap_or_default()
64    }
65
66    pub fn was_predicate(&self, idx: usize) -> bool {
67        self.context
68            .current_to_prev
69            .get(&idx)
70            .map(|old| self.context.predicates.contains(old))
71            .unwrap_or_default()
72    }
73}
74
75#[derive(Copy, Debug, Clone)]
76pub enum ContextKind {
77    Subject,
78    Predicate,
79    Object,
80}
81impl std::fmt::Display for ContextKind {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            ContextKind::Subject => write!(f, "subject"),
85            ContextKind::Predicate => write!(f, "predicate"),
86            ContextKind::Object => write!(f, "object"),
87        }
88    }
89}
90
91#[derive(Debug, Default, Component)]
92pub struct Context {
93    subjects: HashSet<usize>,
94    predicates: HashSet<usize>,
95    objects: HashSet<usize>,
96
97    // This might also be an array, but we keep things simple
98    current_to_prev: HashMap<usize, usize>,
99}
100
101impl Context {
102    pub fn new() -> Self {
103        Self {
104            ..Default::default()
105        }
106    }
107
108    pub fn add_subject(&mut self, idx: usize) {
109        self.subjects.insert(idx);
110    }
111
112    pub fn add_predicate(&mut self, idx: usize) {
113        self.predicates.insert(idx);
114    }
115
116    pub fn add_object(&mut self, idx: usize) {
117        self.objects.insert(idx);
118    }
119
120    pub fn add(&mut self, idx: usize, kind: ContextKind) {
121        match kind {
122            ContextKind::Subject => self.add_subject(idx),
123            ContextKind::Predicate => self.add_predicate(idx),
124            ContextKind::Object => self.add_object(idx),
125        }
126    }
127
128    pub fn clear(&mut self) {
129        self.subjects.clear();
130        self.predicates.clear();
131        self.objects.clear();
132        self.current_to_prev.clear();
133    }
134
135    pub fn setup_current_to_prev<Arr>(
136        &mut self,
137        current: Arr,
138        current_length: usize,
139        prev: Arr,
140        prev_length: usize,
141    ) where
142        Arr: Index<usize>,
143        Arr::Output: PartialEq<Arr::Output> + Ord + std::hash::Hash + Eq + Sized + Clone,
144    {
145        let diffs = similar::capture_diff(
146            similar::Algorithm::Myers,
147            &prev,
148            0..prev_length,
149            &current,
150            0..current_length,
151        );
152        for changes in diffs.iter() {
153            for change in changes.iter_changes(&prev, &current) {
154                if change.tag() == ChangeTag::Equal {
155                    if let (Some(new), Some(old)) = (change.new_index(), change.old_index()) {
156                        self.current_to_prev.insert(new, old);
157                    }
158                }
159            }
160        }
161    }
162
163    pub fn ctx<'a>(&'a self) -> Ctx<'a> {
164        Ctx { context: self }
165    }
166}