1use anyhow::{Result, anyhow};
2use ropey::{Rope, RopeSlice};
3use streaming_iterator::StreamingIterator;
4use tree_sitter::{InputEdit, Point, QueryCursor};
5use tree_sitter::{Language, Parser, Query, Tree, Node};
6use crate::history::{History};
7use crate::selection::Selection;
8use rust_embed::RustEmbed;
9use std::collections::HashMap;
10use crate::utils::{indent, count_indent_units, comment as lang_comment, calculate_end_position};
11use std::cell::RefCell;
12use std::rc::Rc;
13use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
14use unicode_width::{UnicodeWidthStr};
15
16#[derive(RustEmbed)]
17#[folder = ""]
18#[include = "langs/*/*"]
19struct LangAssets;
20
21
22#[derive(Clone)]
23pub enum EditKind {
24 Insert { offset: usize, text: String },
25 Remove { offset: usize, text: String },
26}
27
28#[derive(Clone)]
29pub struct Edit {
30 pub kind: EditKind,
31}
32
33#[derive(Clone)]
34pub struct EditBatch {
35 pub edits: Vec<Edit>,
36 pub state_before: Option<EditState>,
37 pub state_after: Option<EditState>,
38}
39
40impl EditBatch {
41 pub fn new() -> Self {
42 Self {
43 edits: Vec::new(),
44 state_before: None,
45 state_after: None,
46 }
47 }
48
49}
50
51#[derive(Clone, Copy)]
52pub struct EditState {
53 pub offset: usize,
54 pub selection: Option<Selection>,
55}
56
57
58pub struct Code {
59 content: ropey::Rope,
60 lang: String,
61 tree: Option<Tree>,
62 parser: Option<Parser>,
63 query: Option<Query>,
64 applying_history: bool,
65 history: History,
66 current_batch: EditBatch,
67 injection_parsers: Option<HashMap<String, Rc<RefCell<Parser>>>>,
68 injection_queries: Option<HashMap<String, Query>>,
69 change_callback: Option<Box<dyn Fn(Vec<(usize, usize, usize, usize, String)>)>>,
70}
71
72impl Code {
73 pub fn new(text: &str, lang: &str) -> Result<Self> {
75 let (tree, parser, query, injection_parsers, injection_queries) =
76 match Self::get_language(lang) {
77 Some(language) => {
78 let highlights = Self::get_highlights(lang)?;
79 let mut parser = Parser::new();
80 parser.set_language(&language)?;
81 let tree = parser.parse(text, None);
82 let query = Query::new(&language, &highlights)?;
83 let (iparsers, iqueries) = Self::init_injections(&query)?;
84 (tree, Some(parser), Some(query), Some(iparsers), Some(iqueries))
85 }
86 None => (None, None, None, None, None),
87 };
88
89 Ok(Self {
90 content: Rope::from_str(text),
91 lang: lang.to_string(),
92 tree, parser, query,
93 applying_history: true,
94 history: History::new(1000),
95 current_batch: EditBatch::new(),
96 injection_parsers,
97 injection_queries,
98 change_callback: None,
99 })
100 }
101
102 fn get_language(lang: &str) -> Option<Language> {
103 match lang {
104 "rust" => Some(tree_sitter_rust::LANGUAGE.into()),
105 "javascript" => Some(tree_sitter_javascript::LANGUAGE.into()),
106 "typescript" => Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
107 "python" => Some(tree_sitter_python::LANGUAGE.into()),
108 "go" => Some(tree_sitter_go::LANGUAGE.into()),
109 "java" => Some(tree_sitter_java::LANGUAGE.into()),
110 "c_sharp" => Some(tree_sitter_c_sharp::LANGUAGE.into()),
111 "c" => Some(tree_sitter_c::LANGUAGE.into()),
112 "cpp" => Some(tree_sitter_cpp::LANGUAGE.into()),
113 "html" => Some(tree_sitter_html::LANGUAGE.into()),
114 "css" => Some(tree_sitter_css::LANGUAGE.into()),
115 "yaml" => Some(tree_sitter_yaml::LANGUAGE.into()),
116 "json" => Some(tree_sitter_json::LANGUAGE.into()),
117 "toml" => Some(tree_sitter_toml_ng::LANGUAGE.into()),
118 "shell" => Some(tree_sitter_bash::LANGUAGE.into()),
119 "markdown" => Some(tree_sitter_md::LANGUAGE.into()),
120 "markdown-inline" => Some(tree_sitter_md::INLINE_LANGUAGE.into()),
121 _ => None,
122 }
123 }
124
125 fn get_highlights(lang: &str) -> anyhow::Result<String> {
126 let p = format!("langs/{}/highlights.scm", lang);
127 let highlights_bytes =
128 LangAssets::get(&p).ok_or_else(|| anyhow!("No highlights found for {}", lang))?;
129 let highlights_bytes = highlights_bytes.data.as_ref();
130 let highlights = std::str::from_utf8(highlights_bytes)?;
131 Ok(highlights.to_string())
132 }
133
134 fn init_injections(query: &Query) -> anyhow::Result<(
135 HashMap<String, Rc<RefCell<Parser>>>,
136 HashMap<String, Query>,
137 )> {
138 let mut injection_parsers = HashMap::new();
139 let mut injection_queries = HashMap::new();
140
141 for name in query.capture_names() {
142 if let Some(lang) = name.strip_prefix("injection.content.") {
143 if injection_parsers.contains_key(lang) {
144 continue;
145 }
146 if let Some(language) = Self::get_language(lang) {
147 let mut parser = Parser::new();
148 parser.set_language(&language)?;
149 let highlights = Self::get_highlights(lang)?;
150 let inj_query = Query::new(&language, &highlights)?;
151
152 injection_parsers.insert(lang.to_string(), Rc::new(RefCell::new(parser)));
153 injection_queries.insert(lang.to_string(), inj_query);
154 } else {
155 eprintln!("Unknown injection language: {}", lang);
156 }
157 }
158 }
159
160 Ok((injection_parsers, injection_queries))
161 }
162
163 pub fn point(&self, offset: usize) -> (usize, usize) {
164 let row = self.content.char_to_line(offset);
165 let line_start = self.content.line_to_char(row);
166 let col = offset - line_start;
167 (row, col)
168 }
169
170 pub fn offset(&self, row: usize, col: usize) -> usize {
171 let line_start = self.content.line_to_char(row);
172 line_start + col
173 }
174
175 pub fn get_content(&self) -> String {
176 self.content.to_string()
177 }
178
179 pub fn slice(&self, start: usize, end: usize) -> String {
180 self.content.slice(start..end).to_string()
181 }
182
183 pub fn len(&self) -> usize {
184 self.content.len_chars()
185 }
186
187 pub fn len_lines(&self) -> usize {
188 self.content.len_lines()
189 }
190
191 pub fn len_chars(&self) -> usize {
192 self.content.len_chars()
193 }
194
195 pub fn line_to_char(&self, line_idx: usize) -> usize {
196 self.content.line_to_char(line_idx)
197 }
198 pub fn char_to_byte(&self, char_idx: usize) -> usize {
199 self.content.char_to_byte(char_idx)
200 }
201
202 pub fn line_len(&self, idx: usize) -> usize {
203 let line = self.content.line(idx);
204 let len = line.len_chars();
205 if idx == self.content.len_lines() - 1 {
206 len
207 } else {
208 len.saturating_sub(1)
209 }
210 }
211
212 pub fn line(&self, line_idx: usize) -> RopeSlice<'_> {
213 self.content.line(line_idx)
214 }
215
216 pub fn char_to_line(&self, char_idx: usize) -> usize {
217 self.content.char_to_line(char_idx)
218 }
219
220 pub fn char_slice(&self, start: usize, end: usize) -> RopeSlice<'_> {
221 self.content.slice(start..end)
222 }
223
224 pub fn byte_slice(&self, start: usize, end: usize) -> RopeSlice<'_> {
225 self.content.byte_slice(start..end)
226 }
227
228 pub fn byte_to_line(&self, byte_idx: usize) -> usize {
229 self.content.byte_to_line(byte_idx)
230 }
231
232 pub fn byte_to_char(&self, byte_idx: usize) -> usize {
233 self.content.byte_to_char(byte_idx)
234 }
235
236 pub fn tx(&mut self) {
237 self.current_batch = EditBatch::new();
238 }
239
240 pub fn set_state_before(&mut self, offset: usize, selection: Option<Selection>) {
241 self.current_batch.state_before = Some(EditState { offset, selection });
242 }
243
244 pub fn set_state_after(&mut self, offset: usize, selection: Option<Selection>) {
245 self.current_batch.state_after = Some(EditState { offset, selection });
246 }
247
248 pub fn commit(&mut self) {
249 if !self.current_batch.edits.is_empty() {
250 self.notify_changes(&self.current_batch.edits);
251 self.history.push(self.current_batch.clone());
252 self.current_batch = EditBatch::new();
253 }
254 }
255
256 pub fn insert(&mut self, from: usize, text: &str) {
257 let byte_idx = self.content.char_to_byte(from);
258 let byte_len: usize = text.chars().map(|ch| ch.len_utf8()).sum();
259
260 self.content.insert(from, text);
261
262 if self.applying_history {
263 self.current_batch.edits.push(Edit {
264 kind: EditKind::Insert {
265 offset: from,
266 text: text.to_string(),
267 },
268 });
269 }
270
271 if self.tree.is_some() {
272 self.edit_tree(InputEdit {
273 start_byte: byte_idx,
274 old_end_byte: byte_idx,
275 new_end_byte: byte_idx + byte_len,
276 start_position: Point { row: 0, column: 0 },
277 old_end_position: Point { row: 0, column: 0 },
278 new_end_position: Point { row: 0, column: 0 },
279 });
280 }
281 }
282
283 pub fn remove(&mut self, from: usize, to: usize) {
284 let from_byte = self.content.char_to_byte(from);
285 let to_byte = self.content.char_to_byte(to);
286 let removed_text = self.content.slice(from..to).to_string();
287
288 self.content.remove(from..to);
289
290 if self.applying_history {
291 self.current_batch.edits.push(Edit {
292 kind: EditKind::Remove {
293 offset: from,
294 text: removed_text,
295 },
296 });
297 }
298
299 if self.tree.is_some() {
300 self.edit_tree(InputEdit {
301 start_byte: from_byte,
302 old_end_byte: to_byte,
303 new_end_byte: from_byte,
304 start_position: Point { row: 0, column: 0 },
305 old_end_position: Point { row: 0, column: 0 },
306 new_end_position: Point { row: 0, column: 0 },
307 });
308 }
309 }
310
311 fn edit_tree(&mut self, edit: InputEdit) {
312 if let Some(tree) = self.tree.as_mut() {
313 tree.edit(&edit);
314 self.reparse();
315 }
316 }
317
318 fn reparse(&mut self) {
319 if let Some(parser) = self.parser.as_mut() {
320 let rope = &self.content;
321 self.tree = parser.parse_with_options(
322 &mut |byte, _| {
323 if byte <= rope.len_bytes() {
324 let (chunk, start, _, _) = rope.chunk_at_byte(byte);
325 &chunk.as_bytes()[byte - start..]
326 } else {
327 &[]
328 }
329 },
330 self.tree.as_ref(),
331 None,
332 );
333 }
334 }
335
336 pub fn is_highlight(&self) -> bool {
337 self.query.is_some()
338 }
339
340 pub fn highlight_interval<T: Copy>(
343 &self, start: usize, end: usize, theme: &HashMap<String, T>,
344 ) -> Vec<(usize, usize, T)> {
345 if start > end { panic!("Invalid range") }
346
347 let Some(query) = &self.query else { return vec![]; };
348 let Some(tree) = &self.tree else { return vec![]; };
349
350 let text = self.content.slice(..);
351 let root_node = tree.root_node();
352
353 let mut results = Self::highlight(
354 text,
355 start,
356 end,
357 query,
358 root_node,
359 theme,
360 self.injection_parsers.as_ref(),
361 self.injection_queries.as_ref(),
362 );
363
364 results.sort_by(|a, b| {
365 let len_a = a.1 - a.0;
366 let len_b = b.1 - b.0;
367 match len_b.cmp(&len_a) {
368 std::cmp::Ordering::Equal => b.2.cmp(&a.2),
369 other => other,
370 }
371 });
372
373 results
374 .into_iter()
375 .map(|(start, end, _, value)| (start, end, value))
376 .collect()
377 }
378
379 fn highlight<T: Copy>(
380 text: RopeSlice<'_>,
381 start_byte: usize,
382 end_byte: usize,
383 query: &Query,
384 root_node: Node,
385 theme: &HashMap<String, T>,
386 injection_parsers: Option<&HashMap<String, Rc<RefCell<Parser>>>>,
387 injection_queries: Option<&HashMap<String, Query>>,
388 ) -> Vec<(usize, usize, usize, T)> {
389 let mut cursor = QueryCursor::new();
390 cursor.set_byte_range(start_byte..end_byte);
391
392 let mut matches = cursor.matches(query, root_node, RopeProvider(text));
393
394 let mut results = Vec::new();
395 let capture_names = query.capture_names();
396
397 while let Some(m) = matches.next() {
398 for capture in m.captures {
399 let name = capture_names[capture.index as usize];
400 if let Some(value) = theme.get(name) {
401 results.push((
402 capture.node.start_byte(),
403 capture.node.end_byte(),
404 capture.index as usize,
405 *value,
406 ));
407 } else if let Some(lang) = name.strip_prefix("injection.content.") {
408 let Some(injection_parsers) = injection_parsers else { continue };
409 let Some(injection_queries) = injection_queries else { continue };
410 let Some(parser) = injection_parsers.get(lang) else { continue };
411 let Some(injection_query) = injection_queries.get(lang) else { continue };
412
413 let start = capture.node.start_byte();
414 let end = capture.node.end_byte();
415 let slice = text.byte_slice(start..end);
416
417 let mut parser = parser.borrow_mut();
418 let Some(inj_tree) = parser.parse(slice.to_string(), None) else { continue };
419
420 let injection_results = Self::highlight(
421 slice,
422 0,
423 end - start,
424 injection_query,
425 inj_tree.root_node(),
426 theme,
427 injection_parsers.into(),
428 injection_queries.into(),
429 );
430
431 for (s, e, i, v) in injection_results {
432 results.push((s + start, e + start, i, v));
433 }
434 }
435 }
436 }
437
438 results
439 }
440
441
442 pub fn undo(&mut self) -> Option<EditBatch> {
443 let batch = self.history.undo()?;
444 self.applying_history = false;
445
446 for edit in batch.edits.iter().rev() {
447 match edit.kind {
448 EditKind::Insert { offset, ref text } => {
449 self.remove(offset, offset + text.chars().count());
450 }
451 EditKind::Remove { offset, ref text } => {
452 self.insert(offset, text);
453 }
454 }
455 }
456
457 self.applying_history = true;
458 Some(batch)
459 }
460
461 pub fn redo(&mut self) -> Option<EditBatch> {
462 let batch = self.history.redo()?;
463 self.applying_history = false;
464
465 for edit in &batch.edits {
466 match edit.kind {
467 EditKind::Insert { offset, ref text } => {
468 self.insert(offset, text);
469 }
470 EditKind::Remove { offset, ref text } => {
471 self.remove(offset, offset + text.chars().count());
472 }
473 }
474 }
475
476 self.applying_history = true;
477 Some(batch)
478 }
479
480 pub fn word_boundaries(&self, pos: usize) -> (usize, usize) {
481 let len = self.content.len_chars();
482 if pos >= len {
483 return (pos, pos);
484 }
485
486 let is_word_char = |c: char| c.is_alphanumeric() || c == '_';
487
488 let mut start = pos;
489 while start > 0 {
490 let c = self.content.char(start - 1);
491 if !is_word_char(c) {
492 break;
493 }
494 start -= 1;
495 }
496
497 let mut end = pos;
498 while end < len {
499 let c = self.content.char(end);
500 if !is_word_char(c) {
501 break;
502 }
503 end += 1;
504 }
505
506 (start, end)
507 }
508
509 pub fn line_boundaries(&self, pos: usize) -> (usize, usize) {
510 let total_chars = self.content.len_chars();
511 if pos >= total_chars {
512 return (pos, pos);
513 }
514
515 let line = self.content.char_to_line(pos);
516 let start = self.content.line_to_char(line);
517 let end = start + self.content.line(line).len_chars();
518
519 (start, end)
520 }
521
522 pub fn indent(&self) -> String {
523 indent(&self.lang)
524 }
525
526 pub fn comment(&self) -> String {
527 lang_comment(&self.lang).to_string()
528 }
529
530 pub fn indentation_level(&self, line: usize, col: usize) -> usize {
531 if self.lang == "unknown" || self.lang.is_empty() { return 0; }
532 let line_str = self.line(line);
533 count_indent_units(line_str, &self.indent(), Some(col))
534 }
535
536 pub fn is_only_indentation_before(&self, r: usize, c: usize) -> bool {
537 if self.lang == "unknown" || self.lang.is_empty() { return false; }
538 if r >= self.len_lines() || c == 0 { return false; }
539
540 let line = self.line(r);
541 let indent_unit = self.indent();
542
543 if indent_unit.is_empty() {
544 return line.chars().take(c).all(|ch| ch.is_whitespace());
545 }
546
547 let count_units = count_indent_units(line, &indent_unit, Some(c));
548 let only_indent = count_units * indent_unit.chars().count() >= c;
549 only_indent
550 }
551
552 pub fn find_indent_at_line_start(&self, line_idx: usize) -> Option<usize> {
553 if line_idx >= self.len_lines() { return None; }
554
555 let line = self.line(line_idx);
556 let indent_unit = self.indent();
557 if indent_unit.is_empty() { return None; }
558
559 let count_units = count_indent_units(line, &indent_unit, None);
560 let col = count_units * indent_unit.chars().count();
561 if col > 0 { Some(col) } else { None }
562 }
563
564 pub fn smart_paste(&mut self, offset: usize, text: &str) -> usize {
579 let (row, col) = self.point(offset);
580 let base_level = self.indentation_level(row, col);
581 let indent_unit = self.indent();
582
583 if indent_unit.is_empty() {
584 self.insert(offset, text);
585 return text.chars().count();
586 }
587
588 let lines: Vec<&str> = text.lines().collect();
589 if lines.is_empty() {
590 return 0;
591 }
592
593 let mut line_levels = Vec::with_capacity(lines.len());
595 for line in &lines {
596 let mut lvl = 0;
597 let mut rest = *line;
598 while rest.starts_with(&indent_unit) {
599 lvl += 1;
600 rest = &rest[indent_unit.len()..];
601 }
602 line_levels.push(lvl);
603 }
604
605 let mut result = Vec::with_capacity(lines.len());
606
607 let first_line_trimmed = lines[0].trim_start();
608 result.push(first_line_trimmed.to_string());
609
610 let mut prev_nonempty_level = base_level;
611 let mut prev_line_level_in_block = line_levels[0];
612
613 for i in 1..lines.len() {
614 let line = lines[i];
615
616 if line.trim().is_empty() {
617 result.push(line.to_string());
618 continue;
619 }
620
621 let diff = (line_levels[i] as isize - prev_line_level_in_block as isize).clamp(-1, 1);
623 let new_level = (prev_nonempty_level as isize + diff).max(0) as usize;
624 let indents = indent_unit.repeat(new_level);
625 let result_line = format!("{}{}", indents, line.trim_start());
626 result.push(result_line);
627
628 prev_nonempty_level = new_level;
630 prev_line_level_in_block = line_levels[i];
631 }
632
633 let to_insert = result.join("\n");
634 self.insert(offset, &to_insert);
635 to_insert.chars().count()
636 }
637
638 pub fn set_change_callback(&mut self, callback: Box<dyn Fn(Vec<(usize, usize, usize, usize, String)>)>) {
640 self.change_callback = Some(callback);
641 }
642
643 fn notify_changes(&self, edits: &[Edit]) {
645 if let Some(callback) = &self.change_callback {
646 let mut changes = Vec::new();
647
648 for edit in edits {
649 match &edit.kind {
650 EditKind::Insert { offset, text } => {
651 let (start_row, start_col) = self.point(*offset);
652 changes.push((start_row, start_col, start_row, start_col, text.clone()));
653 }
654 EditKind::Remove { offset, text } => {
655 let (start_row, start_col) = self.point(*offset);
656 let (end_row, end_col) = calculate_end_position(start_row, start_col, text);
657 changes.push((start_row, start_col, end_row, end_col, String::new()));
658 }
659 }
660 }
661
662 if !changes.is_empty() {
663 callback(changes);
664 }
665 }
666 }
667
668}
669
670pub struct ChunksBytes<'a> {
673 chunks: ropey::iter::Chunks<'a>,
674}
675
676impl<'a> Iterator for ChunksBytes<'a> {
677 type Item = &'a [u8];
678
679 #[inline]
682 fn next(&mut self) -> Option<Self::Item> {
683 self.chunks.next().map(str::as_bytes)
684 }
685}
686
687pub struct RopeProvider<'a>(pub RopeSlice<'a>);
692
693impl<'a> tree_sitter::TextProvider<&'a [u8]> for RopeProvider<'a> {
694 type I = ChunksBytes<'a>;
695
696 #[inline]
699 fn text(&mut self, node: tree_sitter::Node) -> Self::I {
700 let fragment = self.0.byte_slice(node.start_byte()..node.end_byte());
701 ChunksBytes {
702 chunks: fragment.chunks(),
703 }
704 }
705}
706
707pub struct RopeGraphemes<'a> {
709 text: ropey::RopeSlice<'a>,
710 chunks: ropey::iter::Chunks<'a>,
711 cur_chunk: &'a str,
712 cur_chunk_start: usize,
713 cursor: GraphemeCursor,
714}
715
716impl<'a> RopeGraphemes<'a> {
717 pub fn new<'b>(slice: &RopeSlice<'b>) -> RopeGraphemes<'b> {
718 let mut chunks = slice.chunks();
719 let first_chunk = chunks.next().unwrap_or("");
720 RopeGraphemes {
721 text: *slice,
722 chunks: chunks,
723 cur_chunk: first_chunk,
724 cur_chunk_start: 0,
725 cursor: GraphemeCursor::new(0, slice.len_bytes(), true),
726 }
727 }
728}
729
730impl<'a> Iterator for RopeGraphemes<'a> {
731 type Item = RopeSlice<'a>;
732
733 fn next(&mut self) -> Option<RopeSlice<'a>> {
734 let a = self.cursor.cur_cursor();
735 let b;
736 loop {
737 match self
738 .cursor
739 .next_boundary(self.cur_chunk, self.cur_chunk_start)
740 {
741 Ok(None) => {
742 return None;
743 }
744 Ok(Some(n)) => {
745 b = n;
746 break;
747 }
748 Err(GraphemeIncomplete::NextChunk) => {
749 self.cur_chunk_start += self.cur_chunk.len();
750 self.cur_chunk = self.chunks.next().unwrap_or("");
751 }
752 Err(GraphemeIncomplete::PreContext(idx)) => {
753 let (chunk, byte_idx, _, _) = self.text.chunk_at_byte(idx.saturating_sub(1));
754 self.cursor.provide_context(chunk, byte_idx);
755 }
756 _ => unreachable!(),
757 }
758 }
759
760 if a < self.cur_chunk_start {
761 let a_char = self.text.byte_to_char(a);
762 let b_char = self.text.byte_to_char(b);
763
764 Some(self.text.slice(a_char..b_char))
765 } else {
766 let a2 = a - self.cur_chunk_start;
767 let b2 = b - self.cur_chunk_start;
768 Some((&self.cur_chunk[a2..b2]).into())
769 }
770 }
771}
772
773pub fn grapheme_width_and_chars_len(g: RopeSlice) -> (usize, usize) {
774 if let Some(g_str) = g.as_str() {
775 (UnicodeWidthStr::width(g_str), g_str.chars().count())
776 } else {
777 let g_string = g.to_string();
778 let g_str = g_string.as_str();
779 (UnicodeWidthStr::width(g_str), g_str.chars().count())
780 }
781}
782
783pub fn grapheme_width_and_bytes_len(g: RopeSlice) -> (usize, usize) {
784 if let Some(g_str) = g.as_str() {
785 (UnicodeWidthStr::width(g_str), g_str.len())
786 } else {
787 let g_string = g.to_string();
788 let g_str = g_string.as_str();
789 (UnicodeWidthStr::width(g_str), g_str.len())
790 }
791}
792
793pub fn grapheme_width(g: RopeSlice) -> usize {
794 if let Some(s) = g.as_str() {
795 UnicodeWidthStr::width(s)
796 } else {
797 let s = g.to_string();
798 UnicodeWidthStr::width(s.as_str())
799 }
800}
801
802
803#[cfg(test)]
804mod tests {
805 use super::*;
806
807
808 #[test]
809 fn test_insert() {
810 let mut code = Code::new("", "").unwrap();
811 code.insert(0, "Hello ");
812 code.insert(6, "World");
813 assert_eq!(code.content.to_string(), "Hello World");
814 }
815
816 #[test]
817 fn test_remove() {
818 let mut code = Code::new("Hello World", "").unwrap();
819 code.remove(5, 11);
820 assert_eq!(code.content.to_string(), "Hello");
821 }
822
823 #[test]
824 fn test_undo() {
825 let mut code = Code::new("", "").unwrap();
826
827 code.tx();
828 code.insert(0, "Hello ");
829 code.commit();
830
831 code.tx();
832 code.insert(6, "World");
833 code.commit();
834
835 code.undo();
836 assert_eq!(code.content.to_string(), "Hello ");
837
838 code.undo();
839 assert_eq!(code.content.to_string(), "");
840 }
841
842 #[test]
843 fn test_redo() {
844 let mut code = Code::new("", "").unwrap();
845
846 code.tx();
847 code.insert(0, "Hello");
848 code.commit();
849
850 code.undo();
851 assert_eq!(code.content.to_string(), "");
852
853 code.redo();
854 assert_eq!(code.content.to_string(), "Hello");
855 }
856
857
858 #[test]
859 fn test_indentation_level0() {
860 let mut code = Code::new("", "unknown").unwrap();
861 code.insert(0, " hello world");
862 assert_eq!(code.indentation_level(0, 10), 0);
863 }
864
865 #[test]
866 fn test_indentation_level() {
867 let mut code = Code::new("", "python").unwrap();
868 code.insert(0, " print('Hello, World!')");
869 assert_eq!(code.indentation_level(0, 10), 1);
870 }
871
872 #[test]
873 fn test_indentation_level2() {
874 let mut code = Code::new("", "python").unwrap();
875 code.insert(0, " print('Hello, World!')");
876 assert_eq!(code.indentation_level(0, 10), 2);
877 }
878
879 #[test]
880 fn test_is_only_indentation_before() {
881 let mut code = Code::new("", "python").unwrap();
882 code.insert(0, " print('Hello, World!')");
883 assert_eq!(code.is_only_indentation_before(0, 4), true);
884 assert_eq!(code.is_only_indentation_before(0, 10), false);
885 }
886
887 #[test]
888 fn test_is_only_indentation_before2() {
889 let mut code = Code::new("", "").unwrap();
890 code.insert(0, " Hello, World");
891 assert_eq!(code.is_only_indentation_before(0, 4), false);
892 assert_eq!(code.is_only_indentation_before(0, 10), false);
893 }
894
895 #[test]
896 fn test_smart_paste_1() {
897 let initial = "fn foo() {\n let x = 1;\n \n}";
898 let mut code = Code::new(initial, "rust").unwrap();
899
900 let offset = 30;
901 let paste = "if start == end && start == self.code.len() {\n return;\n}";
902 code.smart_paste(offset, paste);
903
904 let expected = "fn foo() {\n let x = 1;\n if start == end && start == self.code.len() {\n return;\n }\n}";
905 assert_eq!(code.get_content(), expected);
906 }
907
908 #[test]
909 fn test_smart_paste_2() {
910 let initial = "fn foo() {\n let x = 1;\n \n}";
911 let mut code = Code::new(initial, "rust").unwrap();
912
913 let offset = 30;
914 let paste = " if start == end && start == self.code.len() {\n return;\n }";
915 code.smart_paste(offset, paste);
916
917 let expected = "fn foo() {\n let x = 1;\n if start == end && start == self.code.len() {\n return;\n }\n}";
918 assert_eq!(code.get_content(), expected);
919 }
920}