lsp_max_ast/core/document/
mod.rs1use lsp_types_max::PositionEncodingKind;
20use texter::{change::GridIndex, core::text::Text};
21use texter_impl::{change::WrapChange, updateable::WrapTree};
22use tree_sitter::{Point, Tree};
23
24use crate::core::errors::{DocumentError, TreeSitterError};
25
26pub(crate) mod texter_impl;
27
28#[derive(Debug, Clone)]
35pub struct Document {
36 pub texter: Text,
37 pub tree: Tree,
38}
39
40impl Document {
41 pub fn new(source: String, tree: Tree, encoding: Option<&PositionEncodingKind>) -> Self {
45 let texter = match encoding.map(|e| e.as_str()) {
46 Some("utf-8") => Text::new(source),
47 Some("utf-32") => Text::new_utf32(source),
48 _ => Text::new_utf16(source),
49 };
50 Self { texter, tree }
51 }
52
53 pub fn as_str(&self) -> &str {
54 &self.texter.text
55 }
56
57 pub fn as_bytes(&self) -> &[u8] {
58 self.texter.text.as_bytes()
59 }
60
61 pub fn is_empty(&self) -> bool {
62 self.texter.text.is_empty()
63 }
64
65 pub fn update(
73 &mut self,
74 parser: &mut tree_sitter::Parser,
75 changes: &[lsp_types_max::TextDocumentContentChangeEvent],
76 ) -> Result<(), DocumentError> {
77 let mut new_tree = WrapTree::from(&mut self.tree);
78
79 for change in changes {
80 self.texter
81 .update(WrapChange::from(change).change, &mut new_tree)?;
82 }
83
84 self.tree = parser
85 .parse(self.texter.text.as_bytes(), Some(&self.tree))
86 .ok_or_else(|| DocumentError::from(TreeSitterError::TreeSitterParser))?;
87
88 Ok(())
89 }
90
91 pub fn normalize_range(
94 &self,
95 position: &lsp_types_max::Range,
96 ) -> Result<lsp_types_max::Range, DocumentError> {
97 let start = self.normalize_position(&position.start)?;
98 let end = self.normalize_position(&position.end)?;
99
100 Ok(lsp_types_max::Range { start, end })
101 }
102
103 pub fn normalize_position(
106 &self,
107 position: &lsp_types_max::Position,
108 ) -> Result<lsp_types_max::Position, DocumentError> {
109 let mut grid = GridIndex {
110 row: position.line as usize,
111 col: position.character as usize,
112 };
113 grid.normalize(&self.texter)?;
114
115 Ok(lsp_types_max::Position {
116 line: grid.row as u32,
117 character: grid.col as u32,
118 })
119 }
120
121 pub fn denormalize_range(
125 &self,
126 range: &tree_sitter::Range,
127 ) -> Result<lsp_types_max::Range, DocumentError> {
128 let start = self.denormalize_point(range.start_point)?;
129 let end = self.denormalize_point(range.end_point)?;
130 Ok(lsp_types_max::Range {
131 start: lsp_types_max::Position {
132 line: start.row as u32,
133 character: start.column as u32,
134 },
135 end: lsp_types_max::Position {
136 line: end.row as u32,
137 character: end.column as u32,
138 },
139 })
140 }
141
142 pub fn denormalize_point(&self, point: Point) -> Result<Point, DocumentError> {
146 let mut grid = GridIndex::from(point);
147 grid.denormalize(&self.texter)?;
148 Ok(Point::from(grid))
149 }
150}
151
152#[cfg(test)]
153mod test {
154 use super::*;
155 use crate::core::errors::TexterError;
156 use lsp_types_max::{Position, PositionEncodingKind};
157 use rstest::{fixture, rstest};
158 use tree_sitter::Parser;
159
160 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
163 enum Encoding {
164 UTF8,
165 UTF16,
166 UTF32,
167 }
168
169 impl Encoding {
170 fn kind(self) -> PositionEncodingKind {
171 match self {
172 Encoding::UTF8 => PositionEncodingKind::UTF8,
173 Encoding::UTF16 => PositionEncodingKind::UTF16,
174 Encoding::UTF32 => PositionEncodingKind::UTF32,
175 }
176 }
177 }
178
179 #[fixture]
180 fn parser() -> Parser {
181 let mut p = Parser::new();
182 p.set_language(&tree_sitter_html::LANGUAGE.into()).unwrap();
183 p
184 }
185
186 #[rstest]
187 fn normalize(mut parser: Parser) {
188 let source = "Apples\nBashdjad\nashdkasdh\nasdsad";
190 let document = Document::new(
191 source.into(),
192 parser.parse(source, None).unwrap(),
193 Some(&PositionEncodingKind::UTF16),
194 );
195
196 assert_eq!(&document.texter.br_indexes.0, &[0, 6, 15, 25]);
197
198 let normalized = |line, character| {
199 let pos = Position { line, character };
200 document.normalize_position(&pos).unwrap()
201 };
202
203 assert_eq!(
204 normalized(0, 0),
205 Position {
206 line: 0,
207 character: 0
208 }
209 );
210 assert_eq!(
211 normalized(0, 5),
212 Position {
213 line: 0,
214 character: 5
215 }
216 );
217 assert_eq!(
218 normalized(1, 3),
219 Position {
220 line: 1,
221 character: 3
222 }
223 );
224 assert_eq!(
225 normalized(3, 5),
226 Position {
227 line: 3,
228 character: 5
229 }
230 );
231
232 let oob = Position {
234 line: 10,
235 character: 0,
236 };
237 assert!(matches!(
238 document.normalize_position(&oob),
239 Err(DocumentError::Texter(TexterError::TexterError(
240 texter::error::Error::OutOfBoundsRow { .. }
241 )))
242 ));
243
244 assert_eq!(
246 normalized(1, 100),
247 Position {
248 line: 1,
249 character: 8
250 }
251 );
252 }
253
254 #[rstest]
255 #[case(Encoding::UTF8, 20)]
256 #[case(Encoding::UTF16, 10)]
257 #[case(Encoding::UTF32, 10)]
258 fn ts_range_to_range_from_cst(
259 mut parser: Parser,
260 #[case] encoding: Encoding,
261 #[case] end_character: u32,
262 ) {
263 let source = "<div>こんにちは</div>";
264 let tree = parser.parse(source, None).unwrap();
265 let document = Document::new(source.into(), tree.clone(), Some(&encoding.kind()));
266
267 let element = tree.root_node().named_child(0).expect("element");
268 let text_node = element.named_child(1).expect("text node");
269
270 assert_eq!(
271 document.denormalize_range(&text_node.range()).unwrap(),
272 lsp_types_max::Range {
273 start: Position {
274 line: 0,
275 character: 5,
276 },
277 end: Position {
278 line: 0,
279 character: end_character,
280 },
281 }
282 );
283 }
284}