plotnik_langs/
lib.rs

1use std::num::NonZeroU16;
2use std::sync::Arc;
3
4use arborium_tree_sitter::Language;
5
6use plotnik_core::{Cardinality, NodeFieldId, NodeTypeId, NodeTypes, StaticNodeTypes};
7
8pub mod builtin;
9pub mod dynamic;
10
11#[cfg(test)]
12mod lib_tests;
13
14pub use builtin::*;
15
16/// User-facing language type. Works with any language (static or dynamic).
17pub type Lang = Arc<dyn LangImpl>;
18
19/// Trait providing a unified facade for tree-sitter's Language API
20/// combined with our node type constraints.
21pub trait LangImpl: Send + Sync {
22    fn name(&self) -> &str;
23
24    /// Parse source code into a tree-sitter tree.
25    fn parse(&self, source: &str) -> arborium_tree_sitter::Tree;
26
27    fn resolve_named_node(&self, kind: &str) -> Option<NodeTypeId>;
28    fn resolve_anonymous_node(&self, kind: &str) -> Option<NodeTypeId>;
29    fn resolve_field(&self, name: &str) -> Option<NodeFieldId>;
30
31    // Enumeration methods for suggestions
32    fn all_named_node_kinds(&self) -> Vec<&'static str>;
33    fn all_field_names(&self) -> Vec<&'static str>;
34    fn node_type_name(&self, node_type_id: NodeTypeId) -> Option<&'static str>;
35    fn field_name(&self, field_id: NodeFieldId) -> Option<&'static str>;
36    fn fields_for_node_type(&self, node_type_id: NodeTypeId) -> Vec<&'static str>;
37
38    fn is_supertype(&self, node_type_id: NodeTypeId) -> bool;
39    fn subtypes(&self, supertype: NodeTypeId) -> &[u16];
40
41    fn root(&self) -> Option<NodeTypeId>;
42    fn is_extra(&self, node_type_id: NodeTypeId) -> bool;
43
44    fn has_field(&self, node_type_id: NodeTypeId, node_field_id: NodeFieldId) -> bool;
45    fn field_cardinality(
46        &self,
47        node_type_id: NodeTypeId,
48        node_field_id: NodeFieldId,
49    ) -> Option<Cardinality>;
50    fn valid_field_types(
51        &self,
52        node_type_id: NodeTypeId,
53        node_field_id: NodeFieldId,
54    ) -> &[NodeTypeId];
55    fn is_valid_field_type(
56        &self,
57        node_type_id: NodeTypeId,
58        node_field_id: NodeFieldId,
59        child: NodeTypeId,
60    ) -> bool;
61
62    fn children_cardinality(&self, node_type_id: NodeTypeId) -> Option<Cardinality>;
63    fn valid_child_types(&self, node_type_id: NodeTypeId) -> &[NodeTypeId];
64    fn is_valid_child_type(&self, node_type_id: NodeTypeId, child: NodeTypeId) -> bool;
65}
66
67/// Generic language implementation parameterized by node types.
68///
69/// This struct provides a single implementation of `LangImpl` that works with
70/// any `NodeTypes` implementation (static or dynamic).
71#[derive(Debug)]
72pub struct LangInner<N: NodeTypes> {
73    name: String,
74    ts_lang: Language,
75    node_types: N,
76}
77
78impl LangInner<&'static StaticNodeTypes> {
79    pub fn new_static(name: &str, ts_lang: Language, node_types: &'static StaticNodeTypes) -> Self {
80        Self {
81            name: name.to_owned(),
82            ts_lang,
83            node_types,
84        }
85    }
86
87    pub fn node_types(&self) -> &'static StaticNodeTypes {
88        self.node_types
89    }
90}
91
92impl<N: NodeTypes + Send + Sync> LangImpl for LangInner<N> {
93    fn name(&self) -> &str {
94        &self.name
95    }
96
97    fn parse(&self, source: &str) -> arborium_tree_sitter::Tree {
98        let mut parser = arborium_tree_sitter::Parser::new();
99        parser
100            .set_language(&self.ts_lang)
101            .expect("failed to set language");
102        parser.parse(source, None).expect("failed to parse source")
103    }
104
105    fn resolve_named_node(&self, kind: &str) -> Option<NodeTypeId> {
106        let id = self.ts_lang.id_for_node_kind(kind, true);
107        NonZeroU16::new(id)
108    }
109
110    fn resolve_anonymous_node(&self, kind: &str) -> Option<NodeTypeId> {
111        let id = self.ts_lang.id_for_node_kind(kind, false);
112        // Node ID 0 is tree-sitter internal; we never obtain it via cursor walk.
113        NonZeroU16::new(id)
114    }
115
116    fn resolve_field(&self, name: &str) -> Option<NodeFieldId> {
117        self.ts_lang.field_id_for_name(name)
118    }
119
120    fn all_named_node_kinds(&self) -> Vec<&'static str> {
121        let count = self.ts_lang.node_kind_count();
122        (0..count as u16)
123            .filter(|&id| self.ts_lang.node_kind_is_named(id))
124            .filter_map(|id| self.ts_lang.node_kind_for_id(id))
125            .collect()
126    }
127
128    fn all_field_names(&self) -> Vec<&'static str> {
129        let count = self.ts_lang.field_count();
130        (1..=count as u16)
131            .filter_map(|id| self.ts_lang.field_name_for_id(id))
132            .collect()
133    }
134
135    fn node_type_name(&self, node_type_id: NodeTypeId) -> Option<&'static str> {
136        self.ts_lang.node_kind_for_id(node_type_id.get())
137    }
138
139    fn field_name(&self, field_id: NodeFieldId) -> Option<&'static str> {
140        self.ts_lang.field_name_for_id(field_id.get())
141    }
142
143    fn fields_for_node_type(&self, node_type_id: NodeTypeId) -> Vec<&'static str> {
144        let count = self.ts_lang.field_count();
145        (1..=count as u16)
146            .filter_map(|id| {
147                let field_id = std::num::NonZeroU16::new(id)?;
148                if self.node_types.has_field(node_type_id, field_id) {
149                    self.ts_lang.field_name_for_id(id)
150                } else {
151                    None
152                }
153            })
154            .collect()
155    }
156
157    fn is_supertype(&self, node_type_id: NodeTypeId) -> bool {
158        self.ts_lang.node_kind_is_supertype(node_type_id.get())
159    }
160
161    fn subtypes(&self, supertype: NodeTypeId) -> &[u16] {
162        self.ts_lang.subtypes_for_supertype(supertype.get())
163    }
164
165    fn root(&self) -> Option<NodeTypeId> {
166        self.node_types.root()
167    }
168
169    fn is_extra(&self, node_type_id: NodeTypeId) -> bool {
170        self.node_types.is_extra(node_type_id)
171    }
172
173    fn has_field(&self, node_type_id: NodeTypeId, node_field_id: NodeFieldId) -> bool {
174        self.node_types.has_field(node_type_id, node_field_id)
175    }
176
177    fn field_cardinality(
178        &self,
179        node_type_id: NodeTypeId,
180        node_field_id: NodeFieldId,
181    ) -> Option<Cardinality> {
182        self.node_types
183            .field_cardinality(node_type_id, node_field_id)
184    }
185
186    fn valid_field_types(
187        &self,
188        node_type_id: NodeTypeId,
189        node_field_id: NodeFieldId,
190    ) -> &[NodeTypeId] {
191        self.node_types
192            .valid_field_types(node_type_id, node_field_id)
193    }
194
195    fn is_valid_field_type(
196        &self,
197        node_type_id: NodeTypeId,
198        node_field_id: NodeFieldId,
199        child: NodeTypeId,
200    ) -> bool {
201        self.node_types
202            .is_valid_field_type(node_type_id, node_field_id, child)
203    }
204
205    fn children_cardinality(&self, node_type_id: NodeTypeId) -> Option<Cardinality> {
206        self.node_types.children_cardinality(node_type_id)
207    }
208
209    fn valid_child_types(&self, node_type_id: NodeTypeId) -> &[NodeTypeId] {
210        self.node_types.valid_child_types(node_type_id)
211    }
212
213    fn is_valid_child_type(&self, node_type_id: NodeTypeId, child: NodeTypeId) -> bool {
214        self.node_types.is_valid_child_type(node_type_id, child)
215    }
216}