1use crate::commands::{
18 AutoPairsConfig, Command, CommandExecutor, CommandResult, CursorCommand, EditCommand,
19 TextEditSpec, UndoHistoryRestoreError, UndoHistorySnapshot,
20};
21use crate::decorations::{Decoration, DecorationLayerId};
22use crate::delta::TextDelta;
23use crate::intervals::FoldRegion;
24use crate::processing::ProcessingEdit;
25use crate::search::{SearchError, SearchMatch, SearchOptions, find_all};
26use crate::selection_set::selection_direction;
27use crate::snippets::SnippetSession;
28use crate::state::CursorState;
29use crate::{AnchorBias, TextAnchor};
30use crate::{
31 IndentationConfig, LineEnding, LineIndex, Position, Selection, SelectionDirection,
32 TabKeyBehavior, ViewCommand,
33};
34use crate::{StateChange, StateChangeCallback, StateChangeType, WrapIndent, WrapMode};
35use std::collections::{BTreeMap, HashMap};
36use std::ops::Range;
37use std::sync::Arc;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct BufferId(u64);
42
43impl BufferId {
44 pub const fn from_raw(id: u64) -> Self {
48 Self(id)
49 }
50
51 pub fn get(self) -> u64 {
53 self.0
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub struct ViewId(u64);
60
61impl ViewId {
62 pub const fn from_raw(id: u64) -> Self {
66 Self(id)
67 }
68
69 pub fn get(self) -> u64 {
71 self.0
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct BufferMetadata {
78 pub uri: Option<String>,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct OpenBufferResult {
85 pub buffer_id: BufferId,
87 pub view_id: ViewId,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub struct JumpTarget {
94 pub buffer_id: BufferId,
96 pub position: Position,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101struct ViewCore {
102 cursor_position: Position,
103 selection: Option<Selection>,
104 secondary_selections: Vec<Selection>,
105 viewport_width: usize,
106 wrap_mode: WrapMode,
107 wrap_indent: WrapIndent,
108 tab_width: usize,
109 tab_key_behavior: TabKeyBehavior,
110 indentation_config: IndentationConfig,
111 auto_pairs: AutoPairsConfig,
112 snippet_session: Option<SnippetSession>,
113 preferred_x_cells: Option<usize>,
114}
115
116impl ViewCore {
117 fn from_executor(executor: &CommandExecutor) -> Self {
118 let editor = executor.editor();
119 Self {
120 cursor_position: editor.cursor_position(),
121 selection: editor.selection().cloned(),
122 secondary_selections: editor.secondary_selections().to_vec(),
123 viewport_width: editor.viewport_width(),
124 wrap_mode: editor.layout_engine().wrap_mode(),
125 wrap_indent: editor.layout_engine().wrap_indent(),
126 tab_width: editor.layout_engine().tab_width(),
127 tab_key_behavior: executor.tab_key_behavior(),
128 indentation_config: executor.indentation_config().clone(),
129 auto_pairs: executor.auto_pairs_config().clone(),
130 snippet_session: executor.snippet_session().cloned(),
131 preferred_x_cells: executor.preferred_x_cells(),
132 }
133 }
134
135 fn apply_to_executor(&self, executor: &mut CommandExecutor) {
136 let mut invalidate_visual_rows = false;
137 let editor = executor.editor_mut();
138 editor.set_cursor_state(
139 self.cursor_position,
140 self.selection.clone(),
141 self.secondary_selections.clone(),
142 );
143
144 if editor.viewport_width() != self.viewport_width {
145 invalidate_visual_rows = true;
146 }
147
148 let before_wrap_mode = editor.layout_engine().wrap_mode();
149 let before_wrap_indent = editor.layout_engine().wrap_indent();
150 let before_tab_width = editor.layout_engine().tab_width();
151 let before_viewport_width = editor.layout_engine().viewport_width();
152 if before_wrap_mode != self.wrap_mode
153 || before_wrap_indent != self.wrap_indent
154 || before_tab_width != self.tab_width
155 || before_viewport_width != self.viewport_width
156 {
157 invalidate_visual_rows = true;
158 }
159
160 if invalidate_visual_rows {
161 editor.set_view_options(
162 self.viewport_width,
163 self.wrap_mode,
164 self.wrap_indent,
165 self.tab_width,
166 );
167 }
168
169 executor.set_tab_key_behavior(self.tab_key_behavior);
170 executor.set_indentation_config(self.indentation_config.clone());
171 executor.set_auto_pairs_config(self.auto_pairs.clone());
172 executor.set_snippet_session(self.snippet_session.clone());
173 executor.set_preferred_x_cells(self.preferred_x_cells);
174 }
175}
176
177struct BufferEntry {
178 meta: BufferMetadata,
179 executor: CommandExecutor,
180 version: u64,
181 last_text_delta: Option<Arc<TextDelta>>,
182 bookmarks: BookmarkSet,
183 marks: MarkSet,
184}
185
186struct ViewEntry {
187 buffer: BufferId,
188 core: ViewCore,
189 version: u64,
190 callbacks: Vec<StateChangeCallback>,
191 scroll_top: usize,
192 scroll_sub_row_offset: u16,
193 overscan_rows: usize,
194 viewport_height: Option<usize>,
195 last_text_delta: Option<Arc<TextDelta>>,
196 jump_list: JumpList,
197}
198
199#[derive(Debug, Clone, PartialEq, Eq)]
201pub enum WorkspaceError {
202 UriAlreadyOpen(String),
204 BufferNotFound(BufferId),
206 ViewNotFound(ViewId),
208 CommandFailed {
210 view: ViewId,
212 message: String,
214 },
215 ApplyEditsFailed {
217 buffer: BufferId,
219 message: String,
221 },
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
226pub enum WorkspaceUndoHistoryRestoreError {
227 BufferNotFound(BufferId),
229 RestoreFailed {
231 buffer: BufferId,
233 error: UndoHistoryRestoreError,
235 },
236}
237
238impl std::fmt::Display for WorkspaceUndoHistoryRestoreError {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 match self {
241 Self::BufferNotFound(id) => write!(f, "Buffer not found (id={})", id.get()),
242 Self::RestoreFailed { buffer, error } => {
243 write!(
244 f,
245 "Restore undo history failed (buffer={}): {}",
246 buffer.get(),
247 error
248 )
249 }
250 }
251 }
252}
253
254impl std::error::Error for WorkspaceUndoHistoryRestoreError {}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub struct WorkspaceSearchResult {
259 pub id: BufferId,
261 pub uri: Option<String>,
263 pub matches: Vec<SearchMatch>,
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub struct ViewSmoothScrollState {
270 pub top_visual_row: usize,
272 pub sub_row_offset: u16,
274 pub overscan_rows: usize,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq)]
280pub struct WorkspaceViewportState {
281 pub width: usize,
283 pub height: Option<usize>,
285 pub scroll_top: usize,
287 pub visible_lines: Range<usize>,
289 pub total_visual_lines: usize,
291 pub smooth_scroll: ViewSmoothScrollState,
293 pub prefetch_lines: Range<usize>,
295}
296
297fn apply_char_offset_delta(mut offset: usize, delta: &TextDelta) -> usize {
298 for edit in &delta.edits {
299 let start = edit.start;
300 let end = edit.end();
301 let deleted_len = edit.deleted_len();
302 let inserted_len = edit.inserted_len();
303
304 if offset < start {
305 continue;
306 }
307
308 if offset < end {
309 offset = start.saturating_add(inserted_len);
311 continue;
312 }
313
314 if inserted_len >= deleted_len {
316 offset = offset.saturating_add(inserted_len - deleted_len);
317 } else {
318 offset = offset.saturating_sub(deleted_len - inserted_len);
319 }
320 }
321
322 offset
323}
324
325fn apply_position_delta(
326 old_index: &LineIndex,
327 new_index: &LineIndex,
328 pos: Position,
329 delta: &TextDelta,
330) -> Position {
331 let before = old_index.position_to_char_offset(pos.line, pos.column);
332 let after = apply_char_offset_delta(before, delta);
333 let (line, column) = new_index.char_offset_to_position(after);
334 Position::new(line, column)
335}
336
337fn apply_selection_delta(
338 old_index: &LineIndex,
339 new_index: &LineIndex,
340 selection: &Selection,
341 delta: &TextDelta,
342) -> Selection {
343 let start = apply_position_delta(old_index, new_index, selection.start, delta);
344 let end = apply_position_delta(old_index, new_index, selection.end, delta);
345 Selection {
346 start,
347 end,
348 direction: selection_direction(start, end),
349 }
350}
351
352#[derive(Debug, Default, Clone, PartialEq, Eq)]
353struct BookmarkSet {
354 anchors: Vec<TextAnchor>,
355}
356
357impl BookmarkSet {
358 fn toggle_line_start(&mut self, line_start_offset: usize) -> bool {
359 let anchor = TextAnchor::new(line_start_offset, AnchorBias::Left);
360 match self
361 .anchors
362 .binary_search_by_key(&anchor.offset, |a| a.offset)
363 {
364 Ok(idx) => {
365 self.anchors.remove(idx);
366 false
367 }
368 Err(idx) => {
369 self.anchors.insert(idx, anchor);
370 true
371 }
372 }
373 }
374
375 fn clear(&mut self) {
376 self.anchors.clear();
377 }
378
379 fn apply_delta(&mut self, delta: &TextDelta) {
380 for a in &mut self.anchors {
381 a.apply_delta(delta);
382 }
383 self.anchors.sort_by_key(|a| a.offset);
384 self.anchors.dedup_by_key(|a| a.offset);
385 }
386
387 fn line_numbers(&self, line_index: &LineIndex) -> Vec<usize> {
388 let mut lines: Vec<usize> = self
389 .anchors
390 .iter()
391 .map(|a| line_index.char_offset_to_position(a.offset).0)
392 .collect();
393 lines.sort_unstable();
394 lines.dedup();
395 lines
396 }
397
398 fn next_after_line_start(&self, current_line_start: usize) -> Option<TextAnchor> {
399 self.anchors
400 .iter()
401 .copied()
402 .find(|a| a.offset > current_line_start)
403 .or_else(|| self.anchors.first().copied())
404 }
405
406 fn prev_before_line_start(&self, current_line_start: usize) -> Option<TextAnchor> {
407 self.anchors
408 .iter()
409 .copied()
410 .rfind(|a| a.offset < current_line_start)
411 .or_else(|| self.anchors.last().copied())
412 }
413}
414
415#[derive(Debug, Default, Clone, PartialEq, Eq)]
416struct MarkSet {
417 marks: BTreeMap<String, TextAnchor>,
418}
419
420impl MarkSet {
421 fn set(&mut self, name: String, offset: usize) {
422 self.marks
423 .insert(name, TextAnchor::new(offset, AnchorBias::Right));
424 }
425
426 fn get(&self, name: &str) -> Option<TextAnchor> {
427 self.marks.get(name).copied()
428 }
429
430 fn remove(&mut self, name: &str) -> bool {
431 self.marks.remove(name).is_some()
432 }
433
434 fn clear(&mut self) {
435 self.marks.clear();
436 }
437
438 fn names(&self) -> Vec<String> {
439 self.marks.keys().cloned().collect()
440 }
441
442 fn apply_delta(&mut self, delta: &TextDelta) {
443 for anchor in self.marks.values_mut() {
444 anchor.apply_delta(delta);
445 }
446 }
447}
448
449#[derive(Debug, Clone, Copy, PartialEq, Eq)]
450struct JumpEntry {
451 buffer_id: BufferId,
452 anchor: TextAnchor,
453}
454
455#[derive(Debug, Default, Clone, PartialEq, Eq)]
456struct JumpList {
457 back: Vec<JumpEntry>,
458 forward: Vec<JumpEntry>,
459 max_len: usize,
460}
461
462impl JumpList {
463 fn new(max_len: usize) -> Self {
464 Self {
465 back: Vec::new(),
466 forward: Vec::new(),
467 max_len: max_len.max(1),
468 }
469 }
470
471 fn record(&mut self, entry: JumpEntry) {
472 if self.back.last().is_some_and(|last| *last == entry) {
473 return;
474 }
475
476 self.back.push(entry);
477 self.forward.clear();
478
479 if self.back.len() > self.max_len {
480 let overflow = self.back.len() - self.max_len;
481 self.back.drain(0..overflow);
482 }
483 }
484
485 fn back(&mut self, current: JumpEntry) -> Option<JumpEntry> {
486 let target = self.back.pop()?;
487 if !self.forward.last().is_some_and(|last| *last == current) {
488 self.forward.push(current);
489 }
490 Some(target)
491 }
492
493 fn forward(&mut self, current: JumpEntry) -> Option<JumpEntry> {
494 let target = self.forward.pop()?;
495 if !self.back.last().is_some_and(|last| *last == current) {
496 self.back.push(current);
497 }
498 Some(target)
499 }
500
501 fn clear(&mut self) {
502 self.back.clear();
503 self.forward.clear();
504 }
505
506 fn apply_delta(&mut self, buffer_id: BufferId, delta: &TextDelta) {
507 for entry in self
508 .back
509 .iter_mut()
510 .chain(self.forward.iter_mut())
511 .filter(|e| e.buffer_id == buffer_id)
512 {
513 entry.anchor.apply_delta(delta);
514 }
515 }
516}
517
518#[derive(Default)]
520pub struct Workspace {
521 next_buffer_id: u64,
522 buffers: BTreeMap<BufferId, BufferEntry>,
523 uri_to_buffer: HashMap<String, BufferId>,
524
525 next_view_id: u64,
526 views: BTreeMap<ViewId, ViewEntry>,
527 active_view: Option<ViewId>,
528
529 intelligence: crate::WorkspaceIntelligence,
530}
531
532impl std::fmt::Debug for Workspace {
533 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534 f.debug_struct("Workspace")
535 .field("buffer_count", &self.buffers.len())
536 .field("view_count", &self.views.len())
537 .field("uri_count", &self.uri_to_buffer.len())
538 .field("active_view", &self.active_view)
539 .field("intelligence_set_count", &self.intelligence.len())
540 .finish()
541 }
542}
543
544impl Workspace {
545 pub fn new() -> Self {
547 Self::default()
548 }
549
550 pub fn len(&self) -> usize {
552 self.buffers.len()
553 }
554
555 pub fn is_empty(&self) -> bool {
557 self.buffers.is_empty()
558 }
559
560 pub fn view_count(&self) -> usize {
562 self.views.len()
563 }
564
565 pub fn active_view_id(&self) -> Option<ViewId> {
567 self.active_view
568 }
569
570 pub fn active_buffer_id(&self) -> Option<BufferId> {
572 let view_id = self.active_view?;
573 self.views.get(&view_id).map(|v| v.buffer)
574 }
575
576 pub fn intelligence(&self) -> &crate::WorkspaceIntelligence {
578 &self.intelligence
579 }
580
581 pub fn intelligence_mut(&mut self) -> &mut crate::WorkspaceIntelligence {
583 &mut self.intelligence
584 }
585
586 pub fn set_active_view(&mut self, id: ViewId) -> Result<(), WorkspaceError> {
588 if !self.views.contains_key(&id) {
589 return Err(WorkspaceError::ViewNotFound(id));
590 }
591 self.active_view = Some(id);
592 Ok(())
593 }
594
595 pub fn open_buffer(
601 &mut self,
602 uri: Option<String>,
603 text: &str,
604 viewport_width: usize,
605 ) -> Result<OpenBufferResult, WorkspaceError> {
606 if let Some(uri) = uri.as_ref()
607 && self.uri_to_buffer.contains_key(uri)
608 {
609 return Err(WorkspaceError::UriAlreadyOpen(uri.clone()));
610 }
611
612 let buffer_id = BufferId(self.next_buffer_id);
613 self.next_buffer_id = self.next_buffer_id.saturating_add(1);
614
615 let executor = CommandExecutor::new(text, viewport_width);
616 let meta = BufferMetadata { uri: uri.clone() };
617 self.buffers.insert(
618 buffer_id,
619 BufferEntry {
620 meta,
621 executor,
622 version: 0,
623 last_text_delta: None,
624 bookmarks: BookmarkSet::default(),
625 marks: MarkSet::default(),
626 },
627 );
628
629 if let Some(uri) = uri {
630 self.uri_to_buffer.insert(uri, buffer_id);
631 }
632
633 let view_id = self.create_view(buffer_id, viewport_width)?;
634
635 if self.active_view.is_none() {
636 self.active_view = Some(view_id);
637 }
638
639 Ok(OpenBufferResult { buffer_id, view_id })
640 }
641
642 pub fn close_buffer(&mut self, id: BufferId) -> Result<(), WorkspaceError> {
644 let Some(entry) = self.buffers.remove(&id) else {
645 return Err(WorkspaceError::BufferNotFound(id));
646 };
647
648 if let Some(uri) = entry.meta.uri.as_ref() {
649 self.uri_to_buffer.remove(uri);
650 }
651
652 let views_to_remove: Vec<ViewId> = self
653 .views
654 .iter()
655 .filter_map(|(vid, v)| if v.buffer == id { Some(*vid) } else { None })
656 .collect();
657 for view_id in views_to_remove {
658 self.views.remove(&view_id);
659 }
660
661 if self
662 .active_view
663 .is_some_and(|active| !self.views.contains_key(&active))
664 {
665 self.active_view = self.views.keys().next().copied();
666 }
667
668 Ok(())
669 }
670
671 pub fn close_view(&mut self, id: ViewId) -> Result<(), WorkspaceError> {
673 let Some(view) = self.views.remove(&id) else {
674 return Err(WorkspaceError::ViewNotFound(id));
675 };
676
677 if self.active_view == Some(id) {
678 self.active_view = self.views.keys().next().copied();
679 }
680
681 let still_has_views = self.views.values().any(|v| v.buffer == view.buffer);
682 if !still_has_views {
683 self.close_buffer(view.buffer)?;
684 }
685
686 Ok(())
687 }
688
689 pub fn buffer_ids(&self) -> Vec<BufferId> {
691 let mut ids: Vec<BufferId> = self.buffers.keys().copied().collect();
692 ids.sort_by_key(|id| id.get());
693 ids
694 }
695
696 pub fn view_ids(&self) -> Vec<ViewId> {
698 let mut ids: Vec<ViewId> = self.views.keys().copied().collect();
699 ids.sort_by_key(|id| id.get());
700 ids
701 }
702
703 pub fn create_view(
705 &mut self,
706 buffer: BufferId,
707 viewport_width: usize,
708 ) -> Result<ViewId, WorkspaceError> {
709 let Some(buffer_entry) = self.buffers.get_mut(&buffer) else {
710 return Err(WorkspaceError::BufferNotFound(buffer));
711 };
712
713 let mut core = ViewCore::from_executor(&buffer_entry.executor);
716 core.cursor_position = Position::new(0, 0);
717 core.selection = None;
718 core.secondary_selections.clear();
719 core.viewport_width = viewport_width.max(1);
720 core.preferred_x_cells = None;
721
722 let view_id = ViewId(self.next_view_id);
723 self.next_view_id = self.next_view_id.saturating_add(1);
724
725 self.views.insert(
726 view_id,
727 ViewEntry {
728 buffer,
729 core,
730 version: 0,
731 callbacks: Vec::new(),
732 scroll_top: 0,
733 scroll_sub_row_offset: 0,
734 overscan_rows: 0,
735 viewport_height: None,
736 last_text_delta: None,
737 jump_list: JumpList::new(200),
738 },
739 );
740
741 Ok(view_id)
742 }
743
744 pub fn buffer_id_for_uri(&self, uri: &str) -> Option<BufferId> {
746 self.uri_to_buffer.get(uri).copied()
747 }
748
749 pub fn buffer_line_index(&self, buffer_id: BufferId) -> Result<&LineIndex, WorkspaceError> {
751 let Some(buffer) = self.buffers.get(&buffer_id) else {
752 return Err(WorkspaceError::BufferNotFound(buffer_id));
753 };
754 Ok(buffer.executor.editor().line_index())
755 }
756
757 pub fn buffer_char_count(&self, buffer_id: BufferId) -> Result<usize, WorkspaceError> {
759 let Some(buffer) = self.buffers.get(&buffer_id) else {
760 return Err(WorkspaceError::BufferNotFound(buffer_id));
761 };
762 Ok(buffer.executor.editor().char_count())
763 }
764
765 pub fn buffer_text_range(
771 &self,
772 buffer_id: BufferId,
773 start: usize,
774 len: usize,
775 ) -> Result<String, WorkspaceError> {
776 let Some(buffer) = self.buffers.get(&buffer_id) else {
777 return Err(WorkspaceError::BufferNotFound(buffer_id));
778 };
779 Ok(buffer.executor.editor().text_range(start, len))
780 }
781
782 pub fn buffer_decorations(
784 &self,
785 buffer_id: BufferId,
786 ) -> Result<&BTreeMap<DecorationLayerId, Vec<Decoration>>, WorkspaceError> {
787 let Some(buffer) = self.buffers.get(&buffer_id) else {
788 return Err(WorkspaceError::BufferNotFound(buffer_id));
789 };
790 Ok(buffer.executor.editor().decorations())
791 }
792
793 pub fn folding_regions_for_buffer(
795 &self,
796 buffer_id: BufferId,
797 ) -> Result<Vec<FoldRegion>, WorkspaceError> {
798 let Some(buffer) = self.buffers.get(&buffer_id) else {
799 return Err(WorkspaceError::BufferNotFound(buffer_id));
800 };
801 Ok(buffer
802 .executor
803 .editor()
804 .folding_manager()
805 .regions()
806 .to_vec())
807 }
808
809 pub fn buffer_is_modified(&self, buffer_id: BufferId) -> Result<bool, WorkspaceError> {
815 let Some(buffer) = self.buffers.get(&buffer_id) else {
816 return Err(WorkspaceError::BufferNotFound(buffer_id));
817 };
818 Ok(!buffer.executor.is_clean())
819 }
820
821 pub fn line_ending_for_buffer(
823 &self,
824 buffer_id: BufferId,
825 ) -> Result<LineEnding, WorkspaceError> {
826 let Some(buffer) = self.buffers.get(&buffer_id) else {
827 return Err(WorkspaceError::BufferNotFound(buffer_id));
828 };
829 Ok(buffer.executor.line_ending())
830 }
831
832 pub fn set_line_ending_for_buffer(
834 &mut self,
835 buffer_id: BufferId,
836 line_ending: LineEnding,
837 ) -> Result<(), WorkspaceError> {
838 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
839 return Err(WorkspaceError::BufferNotFound(buffer_id));
840 };
841 buffer.executor.set_line_ending(line_ending);
842 Ok(())
843 }
844
845 pub fn is_modified_for_view(&self, view_id: ViewId) -> Result<bool, WorkspaceError> {
847 let buffer_id = self.buffer_id_for_view(view_id)?;
848 self.buffer_is_modified(buffer_id)
849 }
850
851 pub fn line_ending_for_view(&self, view_id: ViewId) -> Result<LineEnding, WorkspaceError> {
853 let buffer_id = self.buffer_id_for_view(view_id)?;
854 self.line_ending_for_buffer(buffer_id)
855 }
856
857 pub fn set_line_ending_for_view(
859 &mut self,
860 view_id: ViewId,
861 line_ending: LineEnding,
862 ) -> Result<(), WorkspaceError> {
863 let buffer_id = self.buffer_id_for_view(view_id)?;
864 self.set_line_ending_for_buffer(buffer_id, line_ending)
865 }
866
867 pub fn mark_saved_for_buffer(&mut self, buffer_id: BufferId) -> Result<(), WorkspaceError> {
869 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
870 return Err(WorkspaceError::BufferNotFound(buffer_id));
871 };
872 buffer.executor.mark_clean();
873 Ok(())
874 }
875
876 pub fn mark_saved_for_view(&mut self, view_id: ViewId) -> Result<(), WorkspaceError> {
878 let buffer_id = self.buffer_id_for_view(view_id)?;
879 self.mark_saved_for_buffer(buffer_id)
880 }
881
882 pub fn undo_history_snapshot_for_buffer(
884 &self,
885 buffer_id: BufferId,
886 ) -> Result<UndoHistorySnapshot, WorkspaceError> {
887 let Some(buffer) = self.buffers.get(&buffer_id) else {
888 return Err(WorkspaceError::BufferNotFound(buffer_id));
889 };
890 Ok(buffer.executor.undo_history_snapshot())
891 }
892
893 pub fn restore_undo_history_for_buffer(
899 &mut self,
900 buffer_id: BufferId,
901 snapshot: UndoHistorySnapshot,
902 ) -> Result<(), WorkspaceUndoHistoryRestoreError> {
903 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
904 return Err(WorkspaceUndoHistoryRestoreError::BufferNotFound(buffer_id));
905 };
906
907 buffer.last_text_delta = None;
908 for view in self.views.values_mut() {
909 if view.buffer == buffer_id {
910 view.last_text_delta = None;
911 }
912 }
913
914 buffer
915 .executor
916 .restore_undo_history(snapshot)
917 .map_err(|err| WorkspaceUndoHistoryRestoreError::RestoreFailed {
918 buffer: buffer_id,
919 error: err,
920 })?;
921
922 Ok(())
923 }
924
925 pub fn buffer_metadata(&self, id: BufferId) -> Option<&BufferMetadata> {
927 self.buffers.get(&id).map(|e| &e.meta)
928 }
929
930 pub fn buffer_id_for_view(&self, id: ViewId) -> Result<BufferId, WorkspaceError> {
932 self.views
933 .get(&id)
934 .map(|v| v.buffer)
935 .ok_or(WorkspaceError::ViewNotFound(id))
936 }
937
938 pub fn cursor_position_for_view(&self, id: ViewId) -> Result<Position, WorkspaceError> {
940 self.views
941 .get(&id)
942 .map(|v| v.core.cursor_position)
943 .ok_or(WorkspaceError::ViewNotFound(id))
944 }
945
946 pub fn selection_for_view(&self, id: ViewId) -> Result<Option<Selection>, WorkspaceError> {
948 self.views
949 .get(&id)
950 .map(|v| v.core.selection.clone())
951 .ok_or(WorkspaceError::ViewNotFound(id))
952 }
953
954 pub fn tab_width_for_view(&self, id: ViewId) -> Result<usize, WorkspaceError> {
956 self.views
957 .get(&id)
958 .map(|v| v.core.tab_width)
959 .ok_or(WorkspaceError::ViewNotFound(id))
960 }
961
962 pub fn viewport_width_for_view(&self, id: ViewId) -> Result<usize, WorkspaceError> {
964 self.views
965 .get(&id)
966 .map(|v| v.core.viewport_width)
967 .ok_or(WorkspaceError::ViewNotFound(id))
968 }
969
970 pub fn wrap_mode_for_view(&self, id: ViewId) -> Result<WrapMode, WorkspaceError> {
972 self.views
973 .get(&id)
974 .map(|v| v.core.wrap_mode)
975 .ok_or(WorkspaceError::ViewNotFound(id))
976 }
977
978 pub fn wrap_indent_for_view(&self, id: ViewId) -> Result<WrapIndent, WorkspaceError> {
980 self.views
981 .get(&id)
982 .map(|v| v.core.wrap_indent)
983 .ok_or(WorkspaceError::ViewNotFound(id))
984 }
985
986 pub fn tab_key_behavior_for_view(&self, id: ViewId) -> Result<TabKeyBehavior, WorkspaceError> {
988 self.views
989 .get(&id)
990 .map(|v| v.core.tab_key_behavior)
991 .ok_or(WorkspaceError::ViewNotFound(id))
992 }
993
994 pub fn indentation_config_for_view(
996 &self,
997 id: ViewId,
998 ) -> Result<IndentationConfig, WorkspaceError> {
999 self.views
1000 .get(&id)
1001 .map(|v| v.core.indentation_config.clone())
1002 .ok_or(WorkspaceError::ViewNotFound(id))
1003 }
1004
1005 pub fn auto_pairs_config_for_view(
1007 &self,
1008 id: ViewId,
1009 ) -> Result<AutoPairsConfig, WorkspaceError> {
1010 self.views
1011 .get(&id)
1012 .map(|v| v.core.auto_pairs.clone())
1013 .ok_or(WorkspaceError::ViewNotFound(id))
1014 }
1015
1016 pub fn cursor_state_for_view(&self, id: ViewId) -> Result<CursorState, WorkspaceError> {
1020 let Some(view) = self.views.get(&id) else {
1021 return Err(WorkspaceError::ViewNotFound(id));
1022 };
1023 let Some(buffer) = self.buffers.get(&view.buffer) else {
1024 return Err(WorkspaceError::BufferNotFound(view.buffer));
1025 };
1026
1027 let line_index = buffer.executor.editor().line_index();
1028
1029 let mut selections: Vec<Selection> =
1030 Vec::with_capacity(1 + view.core.secondary_selections.len());
1031 let primary = view.core.selection.clone().unwrap_or(Selection {
1032 start: view.core.cursor_position,
1033 end: view.core.cursor_position,
1034 direction: SelectionDirection::Forward,
1035 });
1036 selections.push(primary);
1037 selections.extend(view.core.secondary_selections.iter().cloned());
1038
1039 let (selections, primary_selection_index) =
1040 crate::selection_set::normalize_selections(selections, 0);
1041 let primary = selections
1042 .get(primary_selection_index)
1043 .cloned()
1044 .unwrap_or(Selection {
1045 start: view.core.cursor_position,
1046 end: view.core.cursor_position,
1047 direction: SelectionDirection::Forward,
1048 });
1049
1050 let position = primary.end;
1051 let offset = line_index.position_to_char_offset(position.line, position.column);
1052
1053 let selection = if primary.start == primary.end {
1054 None
1055 } else {
1056 Some(primary)
1057 };
1058
1059 let multi_cursors: Vec<Position> = selections
1060 .iter()
1061 .enumerate()
1062 .filter_map(|(idx, sel)| {
1063 if idx == primary_selection_index {
1064 None
1065 } else {
1066 Some(sel.end)
1067 }
1068 })
1069 .collect();
1070
1071 Ok(CursorState {
1072 position,
1073 offset,
1074 multi_cursors,
1075 selection,
1076 selections,
1077 primary_selection_index,
1078 })
1079 }
1080
1081 pub fn scroll_top_for_view(&self, id: ViewId) -> Result<usize, WorkspaceError> {
1083 self.views
1084 .get(&id)
1085 .map(|v| v.scroll_top)
1086 .ok_or(WorkspaceError::ViewNotFound(id))
1087 }
1088
1089 pub fn scroll_sub_row_offset_for_view(&self, id: ViewId) -> Result<u16, WorkspaceError> {
1091 self.views
1092 .get(&id)
1093 .map(|v| v.scroll_sub_row_offset)
1094 .ok_or(WorkspaceError::ViewNotFound(id))
1095 }
1096
1097 pub fn overscan_rows_for_view(&self, id: ViewId) -> Result<usize, WorkspaceError> {
1099 self.views
1100 .get(&id)
1101 .map(|v| v.overscan_rows)
1102 .ok_or(WorkspaceError::ViewNotFound(id))
1103 }
1104
1105 pub fn smooth_scroll_state_for_view(
1107 &self,
1108 id: ViewId,
1109 ) -> Result<ViewSmoothScrollState, WorkspaceError> {
1110 let Some(view) = self.views.get(&id) else {
1111 return Err(WorkspaceError::ViewNotFound(id));
1112 };
1113 Ok(ViewSmoothScrollState {
1114 top_visual_row: view.scroll_top,
1115 sub_row_offset: view.scroll_sub_row_offset,
1116 overscan_rows: view.overscan_rows,
1117 })
1118 }
1119
1120 pub fn set_buffer_uri(
1122 &mut self,
1123 id: BufferId,
1124 uri: Option<String>,
1125 ) -> Result<(), WorkspaceError> {
1126 let Some(entry) = self.buffers.get_mut(&id) else {
1127 return Err(WorkspaceError::BufferNotFound(id));
1128 };
1129
1130 if let Some(next) = uri.as_ref()
1131 && self.uri_to_buffer.contains_key(next)
1132 && entry.meta.uri.as_deref() != Some(next.as_str())
1133 {
1134 return Err(WorkspaceError::UriAlreadyOpen(next.clone()));
1135 }
1136
1137 if let Some(prev) = entry.meta.uri.take() {
1138 self.uri_to_buffer.remove(&prev);
1139 }
1140
1141 if let Some(next) = uri.clone() {
1142 self.uri_to_buffer.insert(next, id);
1143 }
1144
1145 entry.meta.uri = uri;
1146 Ok(())
1147 }
1148
1149 pub fn view_version(&self, id: ViewId) -> Option<u64> {
1151 self.views.get(&id).map(|v| v.version)
1152 }
1153
1154 pub fn last_text_delta_for_view(&self, id: ViewId) -> Option<&Arc<TextDelta>> {
1156 self.views.get(&id)?.last_text_delta.as_ref()
1157 }
1158
1159 pub fn take_last_text_delta_for_view(&mut self, id: ViewId) -> Option<Arc<TextDelta>> {
1161 self.views.get_mut(&id)?.last_text_delta.take()
1162 }
1163
1164 pub fn take_last_text_delta_for_buffer(
1169 &mut self,
1170 id: BufferId,
1171 ) -> Result<Option<Arc<TextDelta>>, WorkspaceError> {
1172 let Some(buffer) = self.buffers.get_mut(&id) else {
1173 return Err(WorkspaceError::BufferNotFound(id));
1174 };
1175 Ok(buffer.last_text_delta.take())
1176 }
1177
1178 pub fn subscribe_view<F>(&mut self, id: ViewId, callback: F) -> Result<(), WorkspaceError>
1180 where
1181 F: FnMut(&StateChange) + Send + 'static,
1182 {
1183 let Some(view) = self.views.get_mut(&id) else {
1184 return Err(WorkspaceError::ViewNotFound(id));
1185 };
1186
1187 view.callbacks.push(Box::new(callback));
1188 Ok(())
1189 }
1190
1191 fn notify_view(
1192 view: &mut ViewEntry,
1193 change_type: StateChangeType,
1194 delta: Option<Arc<TextDelta>>,
1195 ) {
1196 let old_version = view.version;
1197 view.version = view.version.saturating_add(1);
1198
1199 let mut change = StateChange::new(change_type, old_version, view.version);
1200 if let Some(delta) = delta {
1201 change = change.with_text_delta(delta);
1202 }
1203
1204 for cb in &mut view.callbacks {
1205 cb(&change);
1206 }
1207 }
1208
1209 fn command_change_type(command: &Command) -> Option<StateChangeType> {
1210 match command {
1211 Command::Edit(EditCommand::Delete { length: 0, .. }) => None,
1212 Command::Edit(EditCommand::Replace {
1213 length: 0, text, ..
1214 }) if text.is_empty() => None,
1215 Command::Edit(EditCommand::EndUndoGroup) => None,
1216 Command::Edit(_) => Some(StateChangeType::DocumentModified),
1217 Command::Cursor(
1218 CursorCommand::MoveTo { .. }
1219 | CursorCommand::MoveBy { .. }
1220 | CursorCommand::MoveVisualBy { .. }
1221 | CursorCommand::MoveToVisual { .. }
1222 | CursorCommand::MoveToLineStart
1223 | CursorCommand::MoveToLineEnd
1224 | CursorCommand::MoveToVisualLineStart
1225 | CursorCommand::MoveToVisualLineEnd
1226 | CursorCommand::MoveGraphemeLeft
1227 | CursorCommand::MoveGraphemeRight
1228 | CursorCommand::MoveWordLeft
1229 | CursorCommand::MoveWordRight
1230 | CursorCommand::MoveToMatchingBracket
1231 | CursorCommand::FindNext { .. }
1232 | CursorCommand::FindPrev { .. },
1233 ) => Some(StateChangeType::CursorMoved),
1234 Command::Cursor(_) => Some(StateChangeType::SelectionChanged),
1235 Command::View(ViewCommand::ScrollTo { .. } | ViewCommand::GetViewport { .. }) => None,
1236 Command::View(_) => Some(StateChangeType::ViewportChanged),
1237 Command::Style(
1238 crate::StyleCommand::AddStyle { .. }
1239 | crate::StyleCommand::RemoveStyle { .. }
1240 | crate::StyleCommand::UpdateBracketMatchHighlights
1241 | crate::StyleCommand::ClearBracketMatchHighlights,
1242 ) => Some(StateChangeType::StyleChanged),
1243 Command::Style(
1244 crate::StyleCommand::Fold { .. }
1245 | crate::StyleCommand::Unfold { .. }
1246 | crate::StyleCommand::UnfoldAll,
1247 ) => Some(StateChangeType::FoldingChanged),
1248 }
1249 }
1250
1251 pub fn execute(
1257 &mut self,
1258 view_id: ViewId,
1259 command: Command,
1260 ) -> Result<CommandResult, WorkspaceError> {
1261 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1262 return Err(WorkspaceError::ViewNotFound(view_id));
1263 };
1264
1265 let change_type = Self::command_change_type(&command);
1266 if change_type.is_none() {
1267 }
1269
1270 let views = &mut self.views;
1272 let buffers = &mut self.buffers;
1273
1274 let Some(view) = views.get_mut(&view_id) else {
1275 return Err(WorkspaceError::ViewNotFound(view_id));
1276 };
1277 let Some(buffer) = buffers.get_mut(&buffer_id) else {
1278 return Err(WorkspaceError::BufferNotFound(buffer_id));
1279 };
1280
1281 let before_view_core = view.core.clone();
1282 let before_line_index = buffer.executor.editor().line_index().clone();
1283 let before_char_count = buffer.executor.editor().char_count();
1284
1285 view.core.apply_to_executor(&mut buffer.executor);
1287
1288 let result = buffer.executor.execute(command.clone()).map_err(|err| {
1289 WorkspaceError::CommandFailed {
1290 view: view_id,
1291 message: err.to_string(),
1292 }
1293 })?;
1294
1295 view.core = ViewCore::from_executor(&buffer.executor);
1296
1297 let delta = buffer.executor.take_last_text_delta().map(Arc::new);
1298 let after_char_count = buffer.executor.editor().char_count();
1299
1300 let view_changed = view.core != before_view_core;
1302 let buffer_text_changed = delta.is_some()
1303 || after_char_count != before_char_count;
1305
1306 let buffer_derived_changed = matches!(command, Command::Style(_));
1307
1308 if !(view_changed || buffer_text_changed || buffer_derived_changed) {
1309 return Ok(result);
1310 }
1311
1312 let change_type = if buffer_text_changed {
1313 StateChangeType::DocumentModified
1314 } else {
1315 change_type.unwrap_or(StateChangeType::ViewportChanged)
1316 };
1317
1318 if buffer_text_changed || buffer_derived_changed {
1319 let delta_arc = delta.clone();
1321 if let Some(delta_arc) = delta_arc {
1322 buffer.last_text_delta = Some(delta_arc.clone());
1323 for other in views.values_mut() {
1324 if other.buffer != buffer_id {
1325 continue;
1326 }
1327 other.last_text_delta = Some(delta_arc.clone());
1328 }
1329 } else {
1330 buffer.last_text_delta = None;
1331 }
1332
1333 if let Some(ref delta_arc) = delta {
1335 let new_index = buffer.executor.editor().line_index();
1336 for (other_id, other) in views.iter_mut() {
1337 if other.buffer != buffer_id || *other_id == view_id {
1338 continue;
1339 }
1340
1341 other.core.cursor_position = apply_position_delta(
1342 &before_line_index,
1343 new_index,
1344 other.core.cursor_position,
1345 delta_arc,
1346 );
1347
1348 if let Some(ref sel) = other.core.selection {
1349 other.core.selection = Some(apply_selection_delta(
1350 &before_line_index,
1351 new_index,
1352 sel,
1353 delta_arc,
1354 ));
1355 }
1356
1357 for sel in &mut other.core.secondary_selections {
1358 *sel = apply_selection_delta(&before_line_index, new_index, sel, delta_arc);
1359 }
1360
1361 if let Some(ref mut session) = other.core.snippet_session {
1362 session.apply_delta(delta_arc);
1363 }
1364 }
1365
1366 buffer.bookmarks.apply_delta(delta_arc);
1368 buffer.marks.apply_delta(delta_arc);
1369 for other in views.values_mut() {
1370 if other.buffer != buffer_id {
1371 continue;
1372 }
1373 other.jump_list.apply_delta(buffer_id, delta_arc);
1374 }
1375 }
1376
1377 for other in views.values_mut() {
1378 if other.buffer != buffer_id {
1379 continue;
1380 }
1381 Self::notify_view(other, change_type, delta.clone());
1382 }
1383
1384 if buffer_text_changed && let Some(uri) = buffer.meta.uri.as_deref() {
1385 self.intelligence.mark_stale_for_uri(uri);
1386 }
1387
1388 buffer.version = buffer.version.saturating_add(1);
1389 } else {
1390 Self::notify_view(view, change_type, None);
1391 }
1392
1393 Ok(result)
1394 }
1395
1396 pub fn has_active_snippet_session(&self, view_id: ViewId) -> Result<bool, WorkspaceError> {
1401 let Some(view) = self.views.get(&view_id) else {
1402 return Err(WorkspaceError::ViewNotFound(view_id));
1403 };
1404 Ok(view
1405 .core
1406 .snippet_session
1407 .as_ref()
1408 .map(|s| s.is_active())
1409 .unwrap_or(false))
1410 }
1411
1412 pub fn toggle_bookmark_at_cursor_line(
1417 &mut self,
1418 view_id: ViewId,
1419 ) -> Result<bool, WorkspaceError> {
1420 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1421 return Err(WorkspaceError::ViewNotFound(view_id));
1422 };
1423 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1424 return Err(WorkspaceError::BufferNotFound(buffer_id));
1425 };
1426 let Some(view) = self.views.get(&view_id) else {
1427 return Err(WorkspaceError::ViewNotFound(view_id));
1428 };
1429
1430 let line_start = buffer
1431 .executor
1432 .editor()
1433 .line_index()
1434 .position_to_char_offset(view.core.cursor_position.line, 0);
1435
1436 let added = buffer.bookmarks.toggle_line_start(line_start);
1437
1438 for v in self.views.values_mut() {
1439 if v.buffer == buffer_id {
1440 Self::notify_view(v, StateChangeType::NavigationChanged, None);
1441 }
1442 }
1443
1444 Ok(added)
1445 }
1446
1447 pub fn bookmark_lines(&self, buffer_id: BufferId) -> Result<Vec<usize>, WorkspaceError> {
1449 let Some(buffer) = self.buffers.get(&buffer_id) else {
1450 return Err(WorkspaceError::BufferNotFound(buffer_id));
1451 };
1452 Ok(buffer
1453 .bookmarks
1454 .line_numbers(buffer.executor.editor().line_index()))
1455 }
1456
1457 pub fn clear_bookmarks(&mut self, buffer_id: BufferId) -> Result<(), WorkspaceError> {
1459 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1460 return Err(WorkspaceError::BufferNotFound(buffer_id));
1461 };
1462 buffer.bookmarks.clear();
1463
1464 for v in self.views.values_mut() {
1465 if v.buffer == buffer_id {
1466 Self::notify_view(v, StateChangeType::NavigationChanged, None);
1467 }
1468 }
1469
1470 Ok(())
1471 }
1472
1473 fn move_view_cursor_to_anchor(
1474 view: &mut ViewEntry,
1475 buffer: &BufferEntry,
1476 anchor: TextAnchor,
1477 ) -> Position {
1478 let (line, column) = buffer
1479 .executor
1480 .editor()
1481 .line_index()
1482 .char_offset_to_position(anchor.offset);
1483 view.core.cursor_position = Position::new(line, column);
1484 view.core.preferred_x_cells = buffer
1485 .executor
1486 .editor()
1487 .logical_position_to_visual(line, column)
1488 .map(|(_, x)| x);
1489 view.core.selection = None;
1490 view.core.secondary_selections.clear();
1491 view.core.cursor_position
1492 }
1493
1494 pub fn goto_next_bookmark(
1498 &mut self,
1499 view_id: ViewId,
1500 ) -> Result<Option<Position>, WorkspaceError> {
1501 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1502 return Err(WorkspaceError::ViewNotFound(view_id));
1503 };
1504 let Some(buffer) = self.buffers.get(&buffer_id) else {
1505 return Err(WorkspaceError::BufferNotFound(buffer_id));
1506 };
1507
1508 let current_line_start = buffer
1509 .executor
1510 .editor()
1511 .line_index()
1512 .position_to_char_offset(
1513 self.views
1514 .get(&view_id)
1515 .ok_or(WorkspaceError::ViewNotFound(view_id))?
1516 .core
1517 .cursor_position
1518 .line,
1519 0,
1520 );
1521
1522 let Some(target) = buffer.bookmarks.next_after_line_start(current_line_start) else {
1523 return Ok(None);
1524 };
1525
1526 let Some(view) = self.views.get_mut(&view_id) else {
1527 return Err(WorkspaceError::ViewNotFound(view_id));
1528 };
1529 let pos = Self::move_view_cursor_to_anchor(view, buffer, target);
1530 Self::notify_view(view, StateChangeType::SelectionChanged, None);
1531 Ok(Some(pos))
1532 }
1533
1534 pub fn goto_prev_bookmark(
1538 &mut self,
1539 view_id: ViewId,
1540 ) -> Result<Option<Position>, WorkspaceError> {
1541 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1542 return Err(WorkspaceError::ViewNotFound(view_id));
1543 };
1544 let Some(buffer) = self.buffers.get(&buffer_id) else {
1545 return Err(WorkspaceError::BufferNotFound(buffer_id));
1546 };
1547
1548 let current_line_start = buffer
1549 .executor
1550 .editor()
1551 .line_index()
1552 .position_to_char_offset(
1553 self.views
1554 .get(&view_id)
1555 .ok_or(WorkspaceError::ViewNotFound(view_id))?
1556 .core
1557 .cursor_position
1558 .line,
1559 0,
1560 );
1561
1562 let Some(target) = buffer.bookmarks.prev_before_line_start(current_line_start) else {
1563 return Ok(None);
1564 };
1565
1566 let Some(view) = self.views.get_mut(&view_id) else {
1567 return Err(WorkspaceError::ViewNotFound(view_id));
1568 };
1569 let pos = Self::move_view_cursor_to_anchor(view, buffer, target);
1570 Self::notify_view(view, StateChangeType::SelectionChanged, None);
1571 Ok(Some(pos))
1572 }
1573
1574 pub fn set_mark_at_cursor(
1576 &mut self,
1577 view_id: ViewId,
1578 name: String,
1579 ) -> Result<(), WorkspaceError> {
1580 if name.trim().is_empty() {
1581 return Err(WorkspaceError::CommandFailed {
1582 view: view_id,
1583 message: "Mark name cannot be empty".to_string(),
1584 });
1585 }
1586
1587 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1588 return Err(WorkspaceError::ViewNotFound(view_id));
1589 };
1590 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1591 return Err(WorkspaceError::BufferNotFound(buffer_id));
1592 };
1593 let Some(view) = self.views.get(&view_id) else {
1594 return Err(WorkspaceError::ViewNotFound(view_id));
1595 };
1596
1597 let pos = view.core.cursor_position;
1598 let offset = buffer
1599 .executor
1600 .editor()
1601 .line_index()
1602 .position_to_char_offset(pos.line, pos.column);
1603 buffer.marks.set(name, offset);
1604
1605 for v in self.views.values_mut() {
1606 if v.buffer == buffer_id {
1607 Self::notify_view(v, StateChangeType::NavigationChanged, None);
1608 }
1609 }
1610
1611 Ok(())
1612 }
1613
1614 pub fn goto_mark(
1618 &mut self,
1619 view_id: ViewId,
1620 name: &str,
1621 ) -> Result<Option<Position>, WorkspaceError> {
1622 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1623 return Err(WorkspaceError::ViewNotFound(view_id));
1624 };
1625 let Some(buffer) = self.buffers.get(&buffer_id) else {
1626 return Err(WorkspaceError::BufferNotFound(buffer_id));
1627 };
1628
1629 let Some(anchor) = buffer.marks.get(name) else {
1630 return Ok(None);
1631 };
1632
1633 let Some(view) = self.views.get_mut(&view_id) else {
1634 return Err(WorkspaceError::ViewNotFound(view_id));
1635 };
1636 let pos = Self::move_view_cursor_to_anchor(view, buffer, anchor);
1637 Self::notify_view(view, StateChangeType::SelectionChanged, None);
1638 Ok(Some(pos))
1639 }
1640
1641 pub fn clear_mark(&mut self, buffer_id: BufferId, name: &str) -> Result<bool, WorkspaceError> {
1645 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1646 return Err(WorkspaceError::BufferNotFound(buffer_id));
1647 };
1648 let existed = buffer.marks.remove(name);
1649 if existed {
1650 for v in self.views.values_mut() {
1651 if v.buffer == buffer_id {
1652 Self::notify_view(v, StateChangeType::NavigationChanged, None);
1653 }
1654 }
1655 }
1656 Ok(existed)
1657 }
1658
1659 pub fn mark_names(&self, buffer_id: BufferId) -> Result<Vec<String>, WorkspaceError> {
1661 let Some(buffer) = self.buffers.get(&buffer_id) else {
1662 return Err(WorkspaceError::BufferNotFound(buffer_id));
1663 };
1664 Ok(buffer.marks.names())
1665 }
1666
1667 pub fn clear_all_marks(&mut self, buffer_id: BufferId) -> Result<(), WorkspaceError> {
1669 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1670 return Err(WorkspaceError::BufferNotFound(buffer_id));
1671 };
1672 buffer.marks.clear();
1673 for v in self.views.values_mut() {
1674 if v.buffer == buffer_id {
1675 Self::notify_view(v, StateChangeType::NavigationChanged, None);
1676 }
1677 }
1678 Ok(())
1679 }
1680
1681 pub fn push_jump_location(&mut self, view_id: ViewId) -> Result<(), WorkspaceError> {
1686 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1687 return Err(WorkspaceError::ViewNotFound(view_id));
1688 };
1689 let Some(buffer) = self.buffers.get(&buffer_id) else {
1690 return Err(WorkspaceError::BufferNotFound(buffer_id));
1691 };
1692 let Some(view) = self.views.get_mut(&view_id) else {
1693 return Err(WorkspaceError::ViewNotFound(view_id));
1694 };
1695
1696 let pos = view.core.cursor_position;
1697 let offset = buffer
1698 .executor
1699 .editor()
1700 .line_index()
1701 .position_to_char_offset(pos.line, pos.column);
1702
1703 view.jump_list.record(JumpEntry {
1704 buffer_id,
1705 anchor: TextAnchor::new(offset, AnchorBias::Right),
1706 });
1707
1708 Self::notify_view(view, StateChangeType::NavigationChanged, None);
1709 Ok(())
1710 }
1711
1712 pub fn jump_back(&mut self, view_id: ViewId) -> Result<Option<JumpTarget>, WorkspaceError> {
1717 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1718 return Err(WorkspaceError::ViewNotFound(view_id));
1719 };
1720 let Some(buffer) = self.buffers.get(&buffer_id) else {
1721 return Err(WorkspaceError::BufferNotFound(buffer_id));
1722 };
1723
1724 let current_pos = self
1725 .views
1726 .get(&view_id)
1727 .ok_or(WorkspaceError::ViewNotFound(view_id))?
1728 .core
1729 .cursor_position;
1730 let current_offset = buffer
1731 .executor
1732 .editor()
1733 .line_index()
1734 .position_to_char_offset(current_pos.line, current_pos.column);
1735 let current = JumpEntry {
1736 buffer_id,
1737 anchor: TextAnchor::new(current_offset, AnchorBias::Right),
1738 };
1739
1740 let Some(view) = self.views.get_mut(&view_id) else {
1741 return Err(WorkspaceError::ViewNotFound(view_id));
1742 };
1743 let Some(target) = view.jump_list.back(current) else {
1744 return Ok(None);
1745 };
1746
1747 let Some(target_buffer) = self.buffers.get(&target.buffer_id) else {
1748 Self::notify_view(view, StateChangeType::NavigationChanged, None);
1749 return Ok(None);
1750 };
1751
1752 let (line, column) = target_buffer
1753 .executor
1754 .editor()
1755 .line_index()
1756 .char_offset_to_position(target.anchor.offset);
1757 let target_pos = Position::new(line, column);
1758
1759 let out = JumpTarget {
1760 buffer_id: target.buffer_id,
1761 position: target_pos,
1762 };
1763
1764 if target.buffer_id == buffer_id {
1765 Self::move_view_cursor_to_anchor(view, buffer, target.anchor);
1766 Self::notify_view(view, StateChangeType::SelectionChanged, None);
1767 } else {
1768 Self::notify_view(view, StateChangeType::NavigationChanged, None);
1769 }
1770
1771 Ok(Some(out))
1772 }
1773
1774 pub fn jump_forward(&mut self, view_id: ViewId) -> Result<Option<JumpTarget>, WorkspaceError> {
1779 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1780 return Err(WorkspaceError::ViewNotFound(view_id));
1781 };
1782 let Some(buffer) = self.buffers.get(&buffer_id) else {
1783 return Err(WorkspaceError::BufferNotFound(buffer_id));
1784 };
1785
1786 let current_pos = self
1787 .views
1788 .get(&view_id)
1789 .ok_or(WorkspaceError::ViewNotFound(view_id))?
1790 .core
1791 .cursor_position;
1792 let current_offset = buffer
1793 .executor
1794 .editor()
1795 .line_index()
1796 .position_to_char_offset(current_pos.line, current_pos.column);
1797 let current = JumpEntry {
1798 buffer_id,
1799 anchor: TextAnchor::new(current_offset, AnchorBias::Right),
1800 };
1801
1802 let Some(view) = self.views.get_mut(&view_id) else {
1803 return Err(WorkspaceError::ViewNotFound(view_id));
1804 };
1805 let Some(target) = view.jump_list.forward(current) else {
1806 return Ok(None);
1807 };
1808
1809 let Some(target_buffer) = self.buffers.get(&target.buffer_id) else {
1810 Self::notify_view(view, StateChangeType::NavigationChanged, None);
1811 return Ok(None);
1812 };
1813
1814 let (line, column) = target_buffer
1815 .executor
1816 .editor()
1817 .line_index()
1818 .char_offset_to_position(target.anchor.offset);
1819 let target_pos = Position::new(line, column);
1820
1821 let out = JumpTarget {
1822 buffer_id: target.buffer_id,
1823 position: target_pos,
1824 };
1825
1826 if target.buffer_id == buffer_id {
1827 Self::move_view_cursor_to_anchor(view, buffer, target.anchor);
1828 Self::notify_view(view, StateChangeType::SelectionChanged, None);
1829 } else {
1830 Self::notify_view(view, StateChangeType::NavigationChanged, None);
1831 }
1832
1833 Ok(Some(out))
1834 }
1835
1836 pub fn clear_jump_list(&mut self, view_id: ViewId) -> Result<(), WorkspaceError> {
1838 let Some(view) = self.views.get_mut(&view_id) else {
1839 return Err(WorkspaceError::ViewNotFound(view_id));
1840 };
1841 view.jump_list.clear();
1842 Self::notify_view(view, StateChangeType::NavigationChanged, None);
1843 Ok(())
1844 }
1845
1846 pub fn apply_jump_target(
1849 &mut self,
1850 view_id: ViewId,
1851 target: JumpTarget,
1852 ) -> Result<(), WorkspaceError> {
1853 let Some(view) = self.views.get_mut(&view_id) else {
1854 return Err(WorkspaceError::ViewNotFound(view_id));
1855 };
1856 if view.buffer != target.buffer_id {
1857 return Err(WorkspaceError::CommandFailed {
1858 view: view_id,
1859 message: "JumpTarget buffer does not match view buffer".to_string(),
1860 });
1861 }
1862
1863 let Some(buffer) = self.buffers.get(&view.buffer) else {
1864 return Err(WorkspaceError::BufferNotFound(view.buffer));
1865 };
1866
1867 view.core.cursor_position = target.position;
1868 view.core.preferred_x_cells = buffer
1869 .executor
1870 .editor()
1871 .logical_position_to_visual(target.position.line, target.position.column)
1872 .map(|(_, x)| x);
1873 view.core.selection = None;
1874 view.core.secondary_selections.clear();
1875
1876 Self::notify_view(view, StateChangeType::SelectionChanged, None);
1877 Ok(())
1878 }
1879
1880 pub fn set_viewport_height(
1882 &mut self,
1883 view_id: ViewId,
1884 height: usize,
1885 ) -> Result<(), WorkspaceError> {
1886 let Some(view) = self.views.get_mut(&view_id) else {
1887 return Err(WorkspaceError::ViewNotFound(view_id));
1888 };
1889 view.viewport_height = Some(height);
1890 Ok(())
1891 }
1892
1893 pub fn set_scroll_top(
1895 &mut self,
1896 view_id: ViewId,
1897 scroll_top: usize,
1898 ) -> Result<(), WorkspaceError> {
1899 let Some(view) = self.views.get_mut(&view_id) else {
1900 return Err(WorkspaceError::ViewNotFound(view_id));
1901 };
1902 view.scroll_top = scroll_top;
1903 Ok(())
1904 }
1905
1906 pub fn set_scroll_sub_row_offset(
1908 &mut self,
1909 view_id: ViewId,
1910 sub_row_offset: u16,
1911 ) -> Result<(), WorkspaceError> {
1912 let Some(view) = self.views.get_mut(&view_id) else {
1913 return Err(WorkspaceError::ViewNotFound(view_id));
1914 };
1915 view.scroll_sub_row_offset = sub_row_offset;
1916 Ok(())
1917 }
1918
1919 pub fn set_overscan_rows(
1921 &mut self,
1922 view_id: ViewId,
1923 overscan_rows: usize,
1924 ) -> Result<(), WorkspaceError> {
1925 let Some(view) = self.views.get_mut(&view_id) else {
1926 return Err(WorkspaceError::ViewNotFound(view_id));
1927 };
1928 view.overscan_rows = overscan_rows;
1929 Ok(())
1930 }
1931
1932 pub fn set_smooth_scroll_state(
1934 &mut self,
1935 view_id: ViewId,
1936 state: ViewSmoothScrollState,
1937 ) -> Result<(), WorkspaceError> {
1938 let Some(view) = self.views.get_mut(&view_id) else {
1939 return Err(WorkspaceError::ViewNotFound(view_id));
1940 };
1941 view.scroll_top = state.top_visual_row;
1942 view.scroll_sub_row_offset = state.sub_row_offset;
1943 view.overscan_rows = state.overscan_rows;
1944 Ok(())
1945 }
1946
1947 pub fn viewport_state_for_view(
1949 &mut self,
1950 view_id: ViewId,
1951 ) -> Result<WorkspaceViewportState, WorkspaceError> {
1952 let Some((
1953 buffer_id,
1954 view_core,
1955 scroll_top,
1956 viewport_height,
1957 sub_row_offset,
1958 overscan_rows,
1959 )) = self.views.get(&view_id).map(|v| {
1960 (
1961 v.buffer,
1962 v.core.clone(),
1963 v.scroll_top,
1964 v.viewport_height,
1965 v.scroll_sub_row_offset,
1966 v.overscan_rows,
1967 )
1968 })
1969 else {
1970 return Err(WorkspaceError::ViewNotFound(view_id));
1971 };
1972
1973 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1974 return Err(WorkspaceError::BufferNotFound(buffer_id));
1975 };
1976 view_core.apply_to_executor(&mut buffer.executor);
1977 let editor = buffer.executor.editor();
1978
1979 let total_visual_lines = editor.visual_line_count();
1980 let visible_end = if let Some(height) = viewport_height {
1981 scroll_top.saturating_add(height).min(total_visual_lines)
1982 } else {
1983 total_visual_lines
1984 };
1985 let visible_lines = scroll_top.min(total_visual_lines)..visible_end;
1986 let prefetch_start = visible_lines.start.saturating_sub(overscan_rows);
1987 let prefetch_end = visible_lines
1988 .end
1989 .saturating_add(overscan_rows)
1990 .min(total_visual_lines);
1991
1992 Ok(WorkspaceViewportState {
1993 width: editor.viewport_width(),
1994 height: viewport_height,
1995 scroll_top,
1996 visible_lines,
1997 total_visual_lines,
1998 smooth_scroll: ViewSmoothScrollState {
1999 top_visual_row: scroll_top,
2000 sub_row_offset,
2001 overscan_rows,
2002 },
2003 prefetch_lines: prefetch_start..prefetch_end,
2004 })
2005 }
2006
2007 pub fn total_visual_lines_for_view(
2009 &mut self,
2010 view_id: ViewId,
2011 ) -> Result<usize, WorkspaceError> {
2012 Ok(self.viewport_state_for_view(view_id)?.total_visual_lines)
2013 }
2014
2015 pub fn visual_to_logical_for_view(
2017 &mut self,
2018 view_id: ViewId,
2019 visual_row: usize,
2020 ) -> Result<(usize, usize), WorkspaceError> {
2021 let Some((buffer_id, view_core)) =
2022 self.views.get(&view_id).map(|v| (v.buffer, v.core.clone()))
2023 else {
2024 return Err(WorkspaceError::ViewNotFound(view_id));
2025 };
2026 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2027 return Err(WorkspaceError::BufferNotFound(buffer_id));
2028 };
2029 view_core.apply_to_executor(&mut buffer.executor);
2030 Ok(buffer.executor.editor().visual_to_logical_line(visual_row))
2031 }
2032
2033 pub fn logical_to_visual_for_view(
2035 &mut self,
2036 view_id: ViewId,
2037 line: usize,
2038 column: usize,
2039 ) -> Result<Option<(usize, usize)>, WorkspaceError> {
2040 let Some((buffer_id, view_core)) =
2041 self.views.get(&view_id).map(|v| (v.buffer, v.core.clone()))
2042 else {
2043 return Err(WorkspaceError::ViewNotFound(view_id));
2044 };
2045 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2046 return Err(WorkspaceError::BufferNotFound(buffer_id));
2047 };
2048 view_core.apply_to_executor(&mut buffer.executor);
2049 Ok(buffer
2050 .executor
2051 .editor()
2052 .logical_position_to_visual(line, column))
2053 }
2054
2055 pub fn visual_position_to_logical_for_view(
2057 &mut self,
2058 view_id: ViewId,
2059 visual_row: usize,
2060 x_cells: usize,
2061 ) -> Result<Option<Position>, WorkspaceError> {
2062 let Some((buffer_id, view_core)) =
2063 self.views.get(&view_id).map(|v| (v.buffer, v.core.clone()))
2064 else {
2065 return Err(WorkspaceError::ViewNotFound(view_id));
2066 };
2067 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2068 return Err(WorkspaceError::BufferNotFound(buffer_id));
2069 };
2070 view_core.apply_to_executor(&mut buffer.executor);
2071 Ok(buffer
2072 .executor
2073 .editor()
2074 .visual_position_to_logical(visual_row, x_cells))
2075 }
2076
2077 pub fn buffer_text(&self, buffer_id: BufferId) -> Result<String, WorkspaceError> {
2079 let Some(buffer) = self.buffers.get(&buffer_id) else {
2080 return Err(WorkspaceError::BufferNotFound(buffer_id));
2081 };
2082 Ok(buffer.executor.editor().get_text())
2083 }
2084
2085 pub fn buffer_text_for_saving(&self, buffer_id: BufferId) -> Result<String, WorkspaceError> {
2087 let Some(buffer) = self.buffers.get(&buffer_id) else {
2088 return Err(WorkspaceError::BufferNotFound(buffer_id));
2089 };
2090 let text = buffer.executor.editor().get_text();
2091 Ok(buffer.executor.line_ending().apply_to_text(&text))
2092 }
2093
2094 pub fn text_for_saving_for_view(&self, view_id: ViewId) -> Result<String, WorkspaceError> {
2096 let buffer_id = self.buffer_id_for_view(view_id)?;
2097 self.buffer_text_for_saving(buffer_id)
2098 }
2099
2100 pub fn get_viewport_content_styled(
2102 &mut self,
2103 view_id: ViewId,
2104 start_visual_row: usize,
2105 count: usize,
2106 ) -> Result<crate::HeadlessGrid, WorkspaceError> {
2107 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
2108 return Err(WorkspaceError::ViewNotFound(view_id));
2109 };
2110
2111 let view_core = self
2112 .views
2113 .get(&view_id)
2114 .map(|v| v.core.clone())
2115 .ok_or(WorkspaceError::ViewNotFound(view_id))?;
2116
2117 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2118 return Err(WorkspaceError::BufferNotFound(buffer_id));
2119 };
2120
2121 view_core.apply_to_executor(&mut buffer.executor);
2122 Ok(buffer
2123 .executor
2124 .editor()
2125 .get_headless_grid_styled(start_visual_row, count))
2126 }
2127
2128 pub fn get_minimap_content(
2130 &mut self,
2131 view_id: ViewId,
2132 start_visual_row: usize,
2133 count: usize,
2134 ) -> Result<crate::MinimapGrid, WorkspaceError> {
2135 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
2136 return Err(WorkspaceError::ViewNotFound(view_id));
2137 };
2138
2139 let view_core = self
2140 .views
2141 .get(&view_id)
2142 .map(|v| v.core.clone())
2143 .ok_or(WorkspaceError::ViewNotFound(view_id))?;
2144
2145 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2146 return Err(WorkspaceError::BufferNotFound(buffer_id));
2147 };
2148
2149 view_core.apply_to_executor(&mut buffer.executor);
2150 Ok(buffer
2151 .executor
2152 .editor()
2153 .get_minimap_grid(start_visual_row, count))
2154 }
2155
2156 pub fn get_viewport_content_composed(
2161 &mut self,
2162 view_id: ViewId,
2163 start_visual_row: usize,
2164 count: usize,
2165 ) -> Result<crate::ComposedGrid, WorkspaceError> {
2166 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
2167 return Err(WorkspaceError::ViewNotFound(view_id));
2168 };
2169
2170 let view_core = self
2171 .views
2172 .get(&view_id)
2173 .map(|v| v.core.clone())
2174 .ok_or(WorkspaceError::ViewNotFound(view_id))?;
2175
2176 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2177 return Err(WorkspaceError::BufferNotFound(buffer_id));
2178 };
2179
2180 view_core.apply_to_executor(&mut buffer.executor);
2181 Ok(buffer
2182 .executor
2183 .editor()
2184 .get_headless_grid_composed(start_visual_row, count))
2185 }
2186
2187 pub fn apply_processing_edits<I>(
2189 &mut self,
2190 buffer_id: BufferId,
2191 edits: I,
2192 ) -> Result<(), WorkspaceError>
2193 where
2194 I: IntoIterator<Item = ProcessingEdit>,
2195 {
2196 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2197 return Err(WorkspaceError::BufferNotFound(buffer_id));
2198 };
2199
2200 let mut style_changed = false;
2201 let mut folding_changed = false;
2202 let mut diagnostics_changed = false;
2203 let mut decorations_changed = false;
2204 let mut symbols_changed = false;
2205
2206 for edit in edits {
2207 match edit {
2208 ProcessingEdit::ReplaceStyleLayer { layer, intervals } => {
2209 buffer
2210 .executor
2211 .editor_mut()
2212 .replace_style_layer(layer, intervals);
2213 style_changed = true;
2214 }
2215 ProcessingEdit::ClearStyleLayer { layer } => {
2216 buffer.executor.editor_mut().clear_style_layer(layer);
2217 style_changed = true;
2218 }
2219 ProcessingEdit::ReplaceFoldingRegions {
2220 regions,
2221 preserve_collapsed,
2222 } => {
2223 buffer
2224 .executor
2225 .editor_mut()
2226 .replace_folding_regions(regions, preserve_collapsed);
2227 folding_changed = true;
2228 }
2229 ProcessingEdit::ClearFoldingRegions => {
2230 buffer.executor.editor_mut().clear_derived_folding_regions();
2231 folding_changed = true;
2232 }
2233 ProcessingEdit::ReplaceDiagnostics { diagnostics } => {
2234 buffer
2235 .executor
2236 .editor_mut()
2237 .replace_diagnostics(diagnostics);
2238 diagnostics_changed = true;
2239 }
2240 ProcessingEdit::ClearDiagnostics => {
2241 buffer.executor.editor_mut().clear_diagnostics();
2242 diagnostics_changed = true;
2243 }
2244 ProcessingEdit::ReplaceDecorations { layer, decorations } => {
2245 buffer
2246 .executor
2247 .editor_mut()
2248 .replace_decorations(layer, decorations);
2249 decorations_changed = true;
2250 }
2251 ProcessingEdit::ClearDecorations { layer } => {
2252 buffer.executor.editor_mut().clear_decorations(layer);
2253 decorations_changed = true;
2254 }
2255 ProcessingEdit::ReplaceDocumentSymbols { symbols } => {
2256 buffer
2257 .executor
2258 .editor_mut()
2259 .replace_document_symbols(symbols);
2260 symbols_changed = true;
2261 }
2262 ProcessingEdit::ClearDocumentSymbols => {
2263 buffer.executor.editor_mut().clear_document_symbols();
2264 symbols_changed = true;
2265 }
2266 }
2267 }
2268
2269 let change_type = if folding_changed {
2270 Some(StateChangeType::FoldingChanged)
2271 } else if style_changed {
2272 Some(StateChangeType::StyleChanged)
2273 } else if decorations_changed {
2274 Some(StateChangeType::DecorationsChanged)
2275 } else if diagnostics_changed {
2276 Some(StateChangeType::DiagnosticsChanged)
2277 } else if symbols_changed {
2278 Some(StateChangeType::SymbolsChanged)
2279 } else {
2280 None
2281 };
2282
2283 if let Some(change_type) = change_type {
2284 for view in self.views.values_mut() {
2285 if view.buffer == buffer_id {
2286 Self::notify_view(view, change_type, None);
2287 }
2288 }
2289 buffer.version = buffer.version.saturating_add(1);
2290 }
2291
2292 Ok(())
2293 }
2294
2295 pub fn search_all_open_buffers(
2300 &self,
2301 query: &str,
2302 options: SearchOptions,
2303 ) -> Result<Vec<WorkspaceSearchResult>, SearchError> {
2304 let mut out: Vec<WorkspaceSearchResult> = Vec::new();
2305
2306 for (id, entry) in &self.buffers {
2307 let text = entry.executor.editor().get_text();
2308 let matches = find_all(&text, query, options)?;
2309 if matches.is_empty() {
2310 continue;
2311 }
2312
2313 out.push(WorkspaceSearchResult {
2314 id: *id,
2315 uri: entry.meta.uri.clone(),
2316 matches,
2317 });
2318 }
2319
2320 Ok(out)
2321 }
2322
2323 pub fn apply_text_edits<I>(
2329 &mut self,
2330 edits: I,
2331 ) -> Result<Vec<(BufferId, usize)>, WorkspaceError>
2332 where
2333 I: IntoIterator<Item = (BufferId, Vec<TextEditSpec>)>,
2334 {
2335 let mut by_id: BTreeMap<BufferId, Vec<TextEditSpec>> = BTreeMap::new();
2336 for (id, mut buffer_edits) in edits {
2337 by_id.entry(id).or_default().append(&mut buffer_edits);
2338 }
2339
2340 let mut applied: Vec<(BufferId, usize)> = Vec::new();
2341 for (buffer_id, buffer_edits) in by_id {
2342 let edit_count = buffer_edits.len();
2343 if edit_count == 0 {
2344 continue;
2345 }
2346
2347 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
2348 return Err(WorkspaceError::BufferNotFound(buffer_id));
2349 };
2350
2351 let before_line_index = buffer.executor.editor().line_index().clone();
2352 let before_char_count = buffer.executor.editor().char_count();
2353
2354 let neutral = ViewCore {
2356 cursor_position: Position::new(0, 0),
2357 selection: None,
2358 secondary_selections: Vec::new(),
2359 viewport_width: buffer.executor.editor().viewport_width().max(1),
2360 wrap_mode: buffer.executor.editor().layout_engine().wrap_mode(),
2361 wrap_indent: buffer.executor.editor().layout_engine().wrap_indent(),
2362 tab_width: buffer.executor.editor().layout_engine().tab_width(),
2363 tab_key_behavior: buffer.executor.tab_key_behavior(),
2364 indentation_config: buffer.executor.indentation_config().clone(),
2365 auto_pairs: buffer.executor.auto_pairs_config().clone(),
2366 snippet_session: None,
2367 preferred_x_cells: None,
2368 };
2369 neutral.apply_to_executor(&mut buffer.executor);
2370
2371 buffer
2372 .executor
2373 .execute(Command::Edit(EditCommand::ApplyTextEdits {
2374 edits: buffer_edits,
2375 }))
2376 .map_err(|err| WorkspaceError::ApplyEditsFailed {
2377 buffer: buffer_id,
2378 message: err.to_string(),
2379 })?;
2380
2381 let delta = buffer.executor.take_last_text_delta().map(Arc::new);
2382 let after_char_count = buffer.executor.editor().char_count();
2383 let changed = delta.is_some() || after_char_count != before_char_count;
2384
2385 if changed {
2386 if let Some(uri) = buffer.meta.uri.as_deref() {
2387 self.intelligence.mark_stale_for_uri(uri);
2388 }
2389
2390 if let Some(ref delta_arc) = delta {
2391 buffer.last_text_delta = Some(delta_arc.clone());
2392 let new_index = buffer.executor.editor().line_index();
2393 for view in self.views.values_mut() {
2394 if view.buffer != buffer_id {
2395 continue;
2396 }
2397
2398 view.last_text_delta = Some(delta_arc.clone());
2399
2400 view.core.cursor_position = apply_position_delta(
2401 &before_line_index,
2402 new_index,
2403 view.core.cursor_position,
2404 delta_arc,
2405 );
2406 if let Some(ref sel) = view.core.selection {
2407 view.core.selection = Some(apply_selection_delta(
2408 &before_line_index,
2409 new_index,
2410 sel,
2411 delta_arc,
2412 ));
2413 }
2414 for sel in &mut view.core.secondary_selections {
2415 *sel = apply_selection_delta(
2416 &before_line_index,
2417 new_index,
2418 sel,
2419 delta_arc,
2420 );
2421 }
2422
2423 Self::notify_view(
2424 view,
2425 StateChangeType::DocumentModified,
2426 Some(delta_arc.clone()),
2427 );
2428 }
2429 } else {
2430 buffer.last_text_delta = None;
2431 for view in self.views.values_mut() {
2432 if view.buffer == buffer_id {
2433 Self::notify_view(view, StateChangeType::DocumentModified, None);
2434 }
2435 }
2436 }
2437
2438 buffer.version = buffer.version.saturating_add(1);
2439 }
2440
2441 applied.push((buffer_id, edit_count));
2442 }
2443
2444 Ok(applied)
2445 }
2446}