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