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