1use crate::intervals::{FoldRegion, Interval, StyleId, StyleLayerId};
37use crate::processing::{DocumentProcessor, ProcessingEdit};
38use crate::snapshot::HeadlessGrid;
39use crate::{
40 Command, CommandError, CommandExecutor, CommandResult, CursorCommand, EditCommand, EditorCore,
41 Position, Selection, SelectionDirection, StyleCommand, ViewCommand,
42};
43use std::collections::HashSet;
44use std::ops::Range;
45
46#[derive(Debug, Clone)]
48pub struct DocumentState {
49 pub line_count: usize,
51 pub char_count: usize,
53 pub byte_count: usize,
55 pub is_modified: bool,
57 pub version: u64,
59}
60
61#[derive(Debug, Clone)]
63pub struct CursorState {
64 pub position: Position,
66 pub offset: usize,
68 pub multi_cursors: Vec<Position>,
70 pub selection: Option<Selection>,
72 pub selections: Vec<Selection>,
74 pub primary_selection_index: usize,
76}
77
78#[derive(Debug, Clone)]
80pub struct ViewportState {
81 pub width: usize,
83 pub height: Option<usize>,
85 pub scroll_top: usize,
87 pub visible_lines: Range<usize>,
89}
90
91#[derive(Debug, Clone)]
93pub struct UndoRedoState {
94 pub can_undo: bool,
96 pub can_redo: bool,
98 pub undo_depth: usize,
100 pub redo_depth: usize,
102 pub current_change_group: Option<usize>,
104}
105
106#[derive(Debug, Clone)]
108pub struct FoldingState {
109 pub regions: Vec<FoldRegion>,
111 pub collapsed_line_count: usize,
113 pub visible_logical_lines: usize,
115 pub total_visual_lines: usize,
117}
118
119#[derive(Debug, Clone)]
121pub struct StyleState {
122 pub style_count: usize,
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum StateChangeType {
129 DocumentModified,
131 CursorMoved,
133 SelectionChanged,
135 ViewportChanged,
137 FoldingChanged,
139 StyleChanged,
141}
142
143#[derive(Debug, Clone)]
145pub struct StateChange {
146 pub change_type: StateChangeType,
148 pub old_version: u64,
150 pub new_version: u64,
152 pub affected_region: Option<Range<usize>>,
154}
155
156impl StateChange {
157 pub fn new(change_type: StateChangeType, old_version: u64, new_version: u64) -> Self {
159 Self {
160 change_type,
161 old_version,
162 new_version,
163 affected_region: None,
164 }
165 }
166
167 pub fn with_region(mut self, region: Range<usize>) -> Self {
169 self.affected_region = Some(region);
170 self
171 }
172}
173
174#[derive(Debug, Clone)]
176pub struct EditorState {
177 pub document: DocumentState,
179 pub cursor: CursorState,
181 pub viewport: ViewportState,
183 pub undo_redo: UndoRedoState,
185 pub folding: FoldingState,
187 pub style: StyleState,
189}
190
191pub type StateChangeCallback = Box<dyn FnMut(&StateChange) + Send>;
193
194pub struct EditorStateManager {
241 executor: CommandExecutor,
243 state_version: u64,
245 is_modified: bool,
247 callbacks: Vec<StateChangeCallback>,
249 scroll_top: usize,
251 viewport_height: Option<usize>,
253}
254
255impl EditorStateManager {
256 pub fn new(text: &str, viewport_width: usize) -> Self {
258 Self {
259 executor: CommandExecutor::new(text, viewport_width),
260 state_version: 0,
261 is_modified: false,
262 callbacks: Vec::new(),
263 scroll_top: 0,
264 viewport_height: None,
265 }
266 }
267
268 pub fn empty(viewport_width: usize) -> Self {
270 Self::new("", viewport_width)
271 }
272
273 pub fn editor(&self) -> &EditorCore {
275 self.executor.editor()
276 }
277
278 pub fn editor_mut(&mut self) -> &mut EditorCore {
280 self.executor.editor_mut()
281 }
282
283 pub fn execute(&mut self, command: Command) -> Result<CommandResult, CommandError> {
290 let change_type = Self::change_type_for_command(&command);
291 let is_delete_like = matches!(
292 &command,
293 Command::Edit(EditCommand::Backspace | EditCommand::DeleteForward)
294 );
295
296 let cursor_before = self.executor.editor().cursor_position();
298 let selection_before = self.executor.editor().selection().cloned();
299 let secondary_before = self.executor.editor().secondary_selections().to_vec();
300 let viewport_width_before = self.executor.editor().viewport_width;
301 let char_count_before = self.executor.editor().char_count();
302
303 let result = self.executor.execute(command)?;
304 let char_count_after = self.executor.editor().char_count();
305
306 if let Some(change_type) = change_type {
307 let changed = match change_type {
308 StateChangeType::CursorMoved => {
309 self.executor.editor().cursor_position() != cursor_before
310 || self.executor.editor().secondary_selections()
311 != secondary_before.as_slice()
312 }
313 StateChangeType::SelectionChanged => {
314 self.executor.editor().cursor_position() != cursor_before
315 || self.executor.editor().selection().cloned() != selection_before
316 || self.executor.editor().secondary_selections()
317 != secondary_before.as_slice()
318 }
319 StateChangeType::ViewportChanged => {
320 self.executor.editor().viewport_width != viewport_width_before
321 }
322 StateChangeType::DocumentModified => {
323 if is_delete_like {
326 char_count_after != char_count_before
327 } else {
328 true
329 }
330 }
331 StateChangeType::FoldingChanged | StateChangeType::StyleChanged => true,
333 };
334
335 if changed {
336 if matches!(change_type, StateChangeType::DocumentModified) {
337 let is_modified = !self.executor.is_clean();
338 self.mark_modified_internal(change_type, Some(is_modified));
339 } else {
340 self.mark_modified_internal(change_type, None);
341 }
342 }
343 }
344
345 Ok(result)
346 }
347
348 fn change_type_for_command(command: &Command) -> Option<StateChangeType> {
349 match command {
350 Command::Edit(EditCommand::InsertText { text }) if text.is_empty() => None,
351 Command::Edit(EditCommand::Delete { length: 0, .. }) => None,
352 Command::Edit(EditCommand::Replace {
353 length: 0, text, ..
354 }) if text.is_empty() => None,
355 Command::Edit(EditCommand::EndUndoGroup) => None,
356 Command::Edit(_) => Some(StateChangeType::DocumentModified),
357 Command::Cursor(CursorCommand::MoveTo { .. } | CursorCommand::MoveBy { .. }) => {
358 Some(StateChangeType::CursorMoved)
359 }
360 Command::Cursor(
361 CursorCommand::SetSelection { .. }
362 | CursorCommand::ExtendSelection { .. }
363 | CursorCommand::ClearSelection
364 | CursorCommand::SetSelections { .. }
365 | CursorCommand::ClearSecondarySelections
366 | CursorCommand::SetRectSelection { .. }
367 | CursorCommand::FindNext { .. }
368 | CursorCommand::FindPrev { .. },
369 ) => Some(StateChangeType::SelectionChanged),
370 Command::View(ViewCommand::SetViewportWidth { .. }) => {
371 Some(StateChangeType::ViewportChanged)
372 }
373 Command::View(ViewCommand::ScrollTo { .. } | ViewCommand::GetViewport { .. }) => None,
374 Command::Style(StyleCommand::AddStyle { .. } | StyleCommand::RemoveStyle { .. }) => {
375 Some(StateChangeType::StyleChanged)
376 }
377 Command::Style(
378 StyleCommand::Fold { .. } | StyleCommand::Unfold { .. } | StyleCommand::UnfoldAll,
379 ) => Some(StateChangeType::FoldingChanged),
380 }
381 }
382
383 pub fn version(&self) -> u64 {
385 self.state_version
386 }
387
388 pub fn set_viewport_height(&mut self, height: usize) {
390 self.viewport_height = Some(height);
391 }
392
393 pub fn set_scroll_top(&mut self, scroll_top: usize) {
395 let old_scroll = self.scroll_top;
396 self.scroll_top = scroll_top;
397
398 if old_scroll != scroll_top {
399 self.notify_change(StateChangeType::ViewportChanged);
400 }
401 }
402
403 pub fn get_full_state(&self) -> EditorState {
405 EditorState {
406 document: self.get_document_state(),
407 cursor: self.get_cursor_state(),
408 viewport: self.get_viewport_state(),
409 undo_redo: self.get_undo_redo_state(),
410 folding: self.get_folding_state(),
411 style: self.get_style_state(),
412 }
413 }
414
415 pub fn get_document_state(&self) -> DocumentState {
417 let editor = self.executor.editor();
418 DocumentState {
419 line_count: editor.line_count(),
420 char_count: editor.char_count(),
421 byte_count: editor.get_text().len(),
422 is_modified: self.is_modified,
423 version: self.state_version,
424 }
425 }
426
427 pub fn get_cursor_state(&self) -> CursorState {
429 let editor = self.executor.editor();
430 let mut selections: Vec<Selection> =
431 Vec::with_capacity(1 + editor.secondary_selections().len());
432
433 let primary = editor.selection().cloned().unwrap_or(Selection {
434 start: editor.cursor_position(),
435 end: editor.cursor_position(),
436 direction: SelectionDirection::Forward,
437 });
438 selections.push(primary);
439 selections.extend(editor.secondary_selections().iter().cloned());
440
441 let (selections, primary_selection_index) =
442 crate::selection_set::normalize_selections(selections, 0);
443 let primary = selections
444 .get(primary_selection_index)
445 .cloned()
446 .unwrap_or(Selection {
447 start: editor.cursor_position(),
448 end: editor.cursor_position(),
449 direction: SelectionDirection::Forward,
450 });
451
452 let position = primary.end;
453 let offset = editor
454 .line_index
455 .position_to_char_offset(position.line, position.column);
456
457 let selection = if primary.start == primary.end {
458 None
459 } else {
460 Some(primary)
461 };
462
463 let multi_cursors: Vec<Position> = selections
464 .iter()
465 .enumerate()
466 .filter_map(|(idx, sel)| {
467 if idx == primary_selection_index {
468 None
469 } else {
470 Some(sel.end)
471 }
472 })
473 .collect();
474
475 CursorState {
476 position,
477 offset,
478 multi_cursors,
479 selection,
480 selections,
481 primary_selection_index,
482 }
483 }
484
485 pub fn get_viewport_state(&self) -> ViewportState {
487 let editor = self.executor.editor();
488 let total_visual_lines = editor.visual_line_count();
489 let visible_end = if let Some(height) = self.viewport_height {
490 self.scroll_top + height
491 } else {
492 total_visual_lines
493 };
494
495 ViewportState {
496 width: editor.viewport_width,
497 height: self.viewport_height,
498 scroll_top: self.scroll_top,
499 visible_lines: self.scroll_top..visible_end.min(total_visual_lines),
500 }
501 }
502
503 pub fn get_undo_redo_state(&self) -> UndoRedoState {
505 UndoRedoState {
506 can_undo: self.executor.can_undo(),
507 can_redo: self.executor.can_redo(),
508 undo_depth: self.executor.undo_depth(),
509 redo_depth: self.executor.redo_depth(),
510 current_change_group: self.executor.current_change_group(),
511 }
512 }
513
514 pub fn get_folding_state(&self) -> FoldingState {
516 let editor = self.executor.editor();
517 let regions = editor.folding_manager.regions().to_vec();
518 let collapsed_line_count: usize = regions
519 .iter()
520 .filter(|r| r.is_collapsed)
521 .map(|r| r.end_line - r.start_line)
522 .sum();
523
524 let visible_logical_lines = editor.line_count() - collapsed_line_count;
525
526 FoldingState {
527 regions,
528 collapsed_line_count,
529 visible_logical_lines,
530 total_visual_lines: editor.visual_line_count(),
531 }
532 }
533
534 pub fn get_style_state(&self) -> StyleState {
536 let editor = self.executor.editor();
537 let layered_count: usize = editor.style_layers.values().map(|t| t.len()).sum();
538 StyleState {
539 style_count: editor.interval_tree.len() + layered_count,
540 }
541 }
542
543 pub fn get_styles_in_range(&self, start: usize, end: usize) -> Vec<(usize, usize, StyleId)> {
545 let editor = self.executor.editor();
546 let mut result: Vec<(usize, usize, StyleId)> = editor
547 .interval_tree
548 .query_range(start, end)
549 .iter()
550 .map(|interval| (interval.start, interval.end, interval.style_id))
551 .collect();
552
553 for tree in editor.style_layers.values() {
554 result.extend(
555 tree.query_range(start, end)
556 .iter()
557 .map(|interval| (interval.start, interval.end, interval.style_id)),
558 );
559 }
560
561 result.sort_unstable_by_key(|(s, e, id)| (*s, *e, *id));
562 result
563 }
564
565 pub fn get_styles_at(&self, offset: usize) -> Vec<StyleId> {
567 let editor = self.executor.editor();
568 let mut styles: Vec<StyleId> = editor
569 .interval_tree
570 .query_point(offset)
571 .iter()
572 .map(|interval| interval.style_id)
573 .collect();
574
575 for tree in editor.style_layers.values() {
576 styles.extend(
577 tree.query_point(offset)
578 .iter()
579 .map(|interval| interval.style_id),
580 );
581 }
582
583 styles.sort_unstable();
584 styles.dedup();
585 styles
586 }
587
588 pub fn replace_style_layer(&mut self, layer: StyleLayerId, intervals: Vec<Interval>) {
593 let editor = self.executor.editor_mut();
594
595 if intervals.is_empty() {
596 editor.style_layers.remove(&layer);
597 self.mark_modified(StateChangeType::StyleChanged);
598 return;
599 }
600
601 let tree = editor.style_layers.entry(layer).or_default();
602 tree.clear();
603
604 for interval in intervals {
605 if interval.start < interval.end {
606 tree.insert(interval);
607 }
608 }
609
610 self.mark_modified(StateChangeType::StyleChanged);
611 }
612
613 pub fn clear_style_layer(&mut self, layer: StyleLayerId) {
615 let editor = self.executor.editor_mut();
616 editor.style_layers.remove(&layer);
617 self.mark_modified(StateChangeType::StyleChanged);
618 }
619
620 pub fn replace_folding_regions(
625 &mut self,
626 mut regions: Vec<FoldRegion>,
627 preserve_collapsed: bool,
628 ) {
629 if preserve_collapsed {
630 let collapsed: HashSet<(usize, usize)> = self
631 .editor()
632 .folding_manager
633 .regions()
634 .iter()
635 .filter(|r| r.is_collapsed)
636 .map(|r| (r.start_line, r.end_line))
637 .collect();
638
639 for region in &mut regions {
640 if collapsed.contains(&(region.start_line, region.end_line)) {
641 region.is_collapsed = true;
642 }
643 }
644 }
645
646 self.editor_mut().folding_manager.replace_regions(regions);
647 self.mark_modified(StateChangeType::FoldingChanged);
648 }
649
650 pub fn clear_folding_regions(&mut self) {
652 self.editor_mut().folding_manager.clear();
653 self.mark_modified(StateChangeType::FoldingChanged);
654 }
655
656 pub fn apply_processing_edits<I>(&mut self, edits: I)
658 where
659 I: IntoIterator<Item = ProcessingEdit>,
660 {
661 for edit in edits {
662 match edit {
663 ProcessingEdit::ReplaceStyleLayer { layer, intervals } => {
664 self.replace_style_layer(layer, intervals);
665 }
666 ProcessingEdit::ClearStyleLayer { layer } => {
667 self.clear_style_layer(layer);
668 }
669 ProcessingEdit::ReplaceFoldingRegions {
670 regions,
671 preserve_collapsed,
672 } => {
673 self.replace_folding_regions(regions, preserve_collapsed);
674 }
675 ProcessingEdit::ClearFoldingRegions => {
676 self.clear_folding_regions();
677 }
678 }
679 }
680 }
681
682 pub fn apply_processor<P>(&mut self, processor: &mut P) -> Result<(), P::Error>
684 where
685 P: DocumentProcessor,
686 {
687 let edits = processor.process(self)?;
688 self.apply_processing_edits(edits);
689 Ok(())
690 }
691
692 pub fn get_viewport_content(&self, start_row: usize, count: usize) -> HeadlessGrid {
694 let editor = self.executor.editor();
695 let text = editor.get_text();
696 let generator = crate::SnapshotGenerator::from_text(&text, editor.viewport_width);
697 generator.get_headless_grid(start_row, count)
698 }
699
700 pub fn get_viewport_content_styled(
705 &self,
706 start_visual_row: usize,
707 count: usize,
708 ) -> HeadlessGrid {
709 self.executor
710 .editor()
711 .get_headless_grid_styled(start_visual_row, count)
712 }
713
714 pub fn subscribe<F>(&mut self, callback: F)
716 where
717 F: FnMut(&StateChange) + Send + 'static,
718 {
719 self.callbacks.push(Box::new(callback));
720 }
721
722 pub fn has_changed_since(&self, version: u64) -> bool {
724 self.state_version > version
725 }
726
727 pub fn mark_modified(&mut self, change_type: StateChangeType) {
729 self.mark_modified_internal(change_type, None);
730 }
731
732 fn mark_modified_internal(
733 &mut self,
734 change_type: StateChangeType,
735 is_modified_override: Option<bool>,
736 ) {
737 let old_version = self.state_version;
738 self.state_version += 1;
739
740 if matches!(change_type, StateChangeType::DocumentModified) {
742 self.is_modified = is_modified_override.unwrap_or(true);
743 }
744
745 let change = StateChange::new(change_type, old_version, self.state_version);
746 self.notify_callbacks(&change);
747 }
748
749 pub fn mark_saved(&mut self) {
751 self.executor.mark_clean();
752 self.is_modified = false;
753 }
754
755 fn notify_change(&mut self, change_type: StateChangeType) {
757 let change = StateChange::new(change_type, self.state_version, self.state_version);
758 self.notify_callbacks(&change);
759 }
760
761 fn notify_callbacks(&mut self, change: &StateChange) {
763 for callback in &mut self.callbacks {
764 callback(change);
765 }
766 }
767}
768
769#[cfg(test)]
770mod tests {
771 use super::*;
772
773 #[test]
774 fn test_document_state() {
775 let manager = EditorStateManager::new("Hello World\nLine 2", 80);
776 let doc_state = manager.get_document_state();
777
778 assert_eq!(doc_state.line_count, 2);
779 assert_eq!(doc_state.char_count, 18); assert!(!doc_state.is_modified);
781 assert_eq!(doc_state.version, 0);
782 }
783
784 #[test]
785 fn test_cursor_state() {
786 let manager = EditorStateManager::new("Hello World", 80);
787 let cursor_state = manager.get_cursor_state();
788
789 assert_eq!(cursor_state.position, Position::new(0, 0));
790 assert_eq!(cursor_state.offset, 0);
791 assert!(cursor_state.selection.is_none());
792 }
793
794 #[test]
795 fn test_viewport_state() {
796 let mut manager = EditorStateManager::new("Line 1\nLine 2\nLine 3", 80);
797 manager.set_viewport_height(10);
798 manager.set_scroll_top(1);
799
800 let viewport_state = manager.get_viewport_state();
801
802 assert_eq!(viewport_state.width, 80);
803 assert_eq!(viewport_state.height, Some(10));
804 assert_eq!(viewport_state.scroll_top, 1);
805 assert_eq!(viewport_state.visible_lines, 1..3);
806 }
807
808 #[test]
809 fn test_folding_state() {
810 let manager = EditorStateManager::new("Line 1\nLine 2\nLine 3", 80);
811 let folding_state = manager.get_folding_state();
812
813 assert_eq!(folding_state.regions.len(), 0);
814 assert_eq!(folding_state.collapsed_line_count, 0);
815 assert_eq!(folding_state.visible_logical_lines, 3);
816 }
817
818 #[test]
819 fn test_style_state() {
820 let manager = EditorStateManager::new("Hello World", 80);
821 let style_state = manager.get_style_state();
822
823 assert_eq!(style_state.style_count, 0);
824 }
825
826 #[test]
827 fn test_full_state() {
828 let manager = EditorStateManager::new("Test", 80);
829 let full_state = manager.get_full_state();
830
831 assert_eq!(full_state.document.line_count, 1);
832 assert_eq!(full_state.cursor.position, Position::new(0, 0));
833 assert_eq!(full_state.viewport.width, 80);
834 }
835
836 #[test]
837 fn test_version_tracking() {
838 let mut manager = EditorStateManager::new("Test", 80);
839
840 assert_eq!(manager.version(), 0);
841 assert!(!manager.has_changed_since(0));
842
843 manager.mark_modified(StateChangeType::DocumentModified);
844
845 assert_eq!(manager.version(), 1);
846 assert!(manager.has_changed_since(0));
847 assert!(!manager.has_changed_since(1));
848 }
849
850 #[test]
851 fn test_modification_tracking() {
852 let mut manager = EditorStateManager::new("Test", 80);
853
854 assert!(!manager.get_document_state().is_modified);
855
856 manager.mark_modified(StateChangeType::DocumentModified);
857 assert!(manager.get_document_state().is_modified);
858
859 manager.mark_saved();
860 assert!(!manager.get_document_state().is_modified);
861 }
862
863 #[test]
864 fn test_undo_redo_state_and_dirty_tracking() {
865 let mut manager = EditorStateManager::empty(80);
866
867 let state = manager.get_undo_redo_state();
868 assert!(!state.can_undo);
869 assert!(!state.can_redo);
870
871 manager
872 .execute(Command::Edit(EditCommand::InsertText {
873 text: "abc".to_string(),
874 }))
875 .unwrap();
876
877 assert!(manager.get_document_state().is_modified);
878 let state = manager.get_undo_redo_state();
879 assert!(state.can_undo);
880 assert!(!state.can_redo);
881 assert_eq!(state.undo_depth, 1);
882
883 manager.execute(Command::Edit(EditCommand::Undo)).unwrap();
884 assert!(!manager.get_document_state().is_modified);
885 let state = manager.get_undo_redo_state();
886 assert!(!state.can_undo);
887 assert!(state.can_redo);
888
889 manager.execute(Command::Edit(EditCommand::Redo)).unwrap();
890 assert!(manager.get_document_state().is_modified);
891 let state = manager.get_undo_redo_state();
892 assert!(state.can_undo);
893 assert!(!state.can_redo);
894 }
895
896 #[test]
897 fn test_state_change_callback() {
898 use std::sync::{Arc, Mutex};
899
900 let mut manager = EditorStateManager::new("Test", 80);
901
902 let callback_called = Arc::new(Mutex::new(false));
903 let callback_called_clone = callback_called.clone();
904
905 manager.subscribe(move |_change| {
906 *callback_called_clone.lock().unwrap() = true;
907 });
908
909 manager.mark_modified(StateChangeType::CursorMoved);
910
911 assert!(*callback_called.lock().unwrap());
913 }
914
915 #[test]
916 fn test_execute_cursor_noop_does_not_bump_version() {
917 let mut manager = EditorStateManager::new("A", 80);
918 assert_eq!(manager.version(), 0);
919
920 manager
922 .execute(Command::Cursor(CursorCommand::MoveBy {
923 delta_line: 0,
924 delta_column: -1,
925 }))
926 .unwrap();
927 assert_eq!(manager.editor().cursor_position(), Position::new(0, 0));
928 assert_eq!(manager.version(), 0);
929
930 manager
932 .execute(Command::Cursor(CursorCommand::MoveTo {
933 line: 0,
934 column: usize::MAX,
935 }))
936 .unwrap();
937 assert_eq!(manager.editor().cursor_position(), Position::new(0, 1));
938 assert_eq!(manager.version(), 1);
939
940 let version_before = manager.version();
942 manager
943 .execute(Command::Cursor(CursorCommand::MoveBy {
944 delta_line: 0,
945 delta_column: 1,
946 }))
947 .unwrap();
948 assert_eq!(manager.editor().cursor_position(), Position::new(0, 1));
949 assert_eq!(manager.version(), version_before);
950 }
951
952 #[test]
953 fn test_viewport_height() {
954 let mut manager = EditorStateManager::new("Test", 80);
955
956 assert_eq!(manager.get_viewport_state().height, None);
957
958 manager.set_viewport_height(20);
959 assert_eq!(manager.get_viewport_state().height, Some(20));
960 }
961
962 #[test]
963 fn test_scroll_position() {
964 let mut manager = EditorStateManager::new("Line 1\nLine 2\nLine 3\nLine 4", 80);
965 manager.set_viewport_height(2);
966
967 assert_eq!(manager.get_viewport_state().scroll_top, 0);
968 assert_eq!(manager.get_viewport_state().visible_lines, 0..2);
969
970 manager.set_scroll_top(2);
971 assert_eq!(manager.get_viewport_state().scroll_top, 2);
972 assert_eq!(manager.get_viewport_state().visible_lines, 2..4);
973 }
974
975 #[test]
976 fn test_get_styles() {
977 let mut manager = EditorStateManager::new("Hello World", 80);
978
979 manager
981 .editor_mut()
982 .interval_tree
983 .insert(crate::intervals::Interval::new(0, 5, 1));
984
985 let styles = manager.get_styles_in_range(0, 10);
986 assert_eq!(styles.len(), 1);
987 assert_eq!(styles[0], (0, 5, 1));
988
989 let styles_at = manager.get_styles_at(3);
990 assert_eq!(styles_at.len(), 1);
991 assert_eq!(styles_at[0], 1);
992 }
993
994 #[test]
995 fn test_replace_style_layer_affects_queries() {
996 let mut manager = EditorStateManager::new("Hello", 80);
997
998 manager.replace_style_layer(
999 StyleLayerId::SEMANTIC_TOKENS,
1000 vec![Interval::new(0, 1, 100)],
1001 );
1002
1003 assert_eq!(manager.get_styles_at(0), vec![100]);
1004
1005 manager
1007 .editor_mut()
1008 .interval_tree
1009 .insert(Interval::new(0, 5, 1));
1010
1011 assert_eq!(manager.get_styles_at(0), vec![1, 100]);
1012 }
1013
1014 #[test]
1015 fn test_viewport_content_styled_wraps_and_includes_styles() {
1016 let mut manager = EditorStateManager::new("abcdef", 3);
1017
1018 manager.replace_style_layer(StyleLayerId::SIMPLE_SYNTAX, vec![Interval::new(1, 4, 7)]);
1020
1021 let grid = manager.get_viewport_content_styled(0, 10);
1022 assert_eq!(grid.actual_line_count(), 2);
1023
1024 let line0 = &grid.lines[0];
1025 assert_eq!(line0.logical_line_index, 0);
1026 assert!(!line0.is_wrapped_part);
1027 assert_eq!(line0.cells.len(), 3);
1028 assert_eq!(line0.cells[0].ch, 'a');
1029 assert_eq!(line0.cells[1].ch, 'b');
1030 assert_eq!(line0.cells[2].ch, 'c');
1031 assert_eq!(line0.cells[0].styles, Vec::<StyleId>::new());
1032 assert_eq!(line0.cells[1].styles, vec![7]);
1033 assert_eq!(line0.cells[2].styles, vec![7]);
1034
1035 let line1 = &grid.lines[1];
1036 assert_eq!(line1.logical_line_index, 0);
1037 assert!(line1.is_wrapped_part);
1038 assert_eq!(line1.cells.len(), 3);
1039 assert_eq!(line1.cells[0].ch, 'd');
1040 assert_eq!(line1.cells[0].styles, vec![7]);
1041 assert_eq!(line1.cells[1].ch, 'e');
1042 assert_eq!(line1.cells[1].styles, Vec::<StyleId>::new());
1043 }
1044}