1use crate::commands::{
18 Command, CommandExecutor, CommandResult, CursorCommand, EditCommand, TextEditSpec,
19};
20use crate::delta::TextDelta;
21use crate::processing::ProcessingEdit;
22use crate::search::{SearchError, SearchMatch, SearchOptions, find_all};
23use crate::selection_set::selection_direction;
24use crate::{LineIndex, Position, Selection, TabKeyBehavior, ViewCommand};
25use crate::{StateChange, StateChangeCallback, StateChangeType, WrapIndent, WrapMode};
26use std::collections::{BTreeMap, HashMap, HashSet};
27use std::ops::Range;
28use std::sync::Arc;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32pub struct BufferId(u64);
33
34impl BufferId {
35 pub const fn from_raw(id: u64) -> Self {
39 Self(id)
40 }
41
42 pub fn get(self) -> u64 {
44 self.0
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub struct ViewId(u64);
51
52impl ViewId {
53 pub const fn from_raw(id: u64) -> Self {
57 Self(id)
58 }
59
60 pub fn get(self) -> u64 {
62 self.0
63 }
64}
65
66#[derive(Debug, Clone)]
68pub struct BufferMetadata {
69 pub uri: Option<String>,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct OpenBufferResult {
76 pub buffer_id: BufferId,
78 pub view_id: ViewId,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83struct ViewCore {
84 cursor_position: Position,
85 selection: Option<Selection>,
86 secondary_selections: Vec<Selection>,
87 viewport_width: usize,
88 wrap_mode: WrapMode,
89 wrap_indent: WrapIndent,
90 tab_width: usize,
91 tab_key_behavior: TabKeyBehavior,
92 preferred_x_cells: Option<usize>,
93}
94
95impl ViewCore {
96 fn from_executor(executor: &CommandExecutor) -> Self {
97 let editor = executor.editor();
98 Self {
99 cursor_position: editor.cursor_position,
100 selection: editor.selection.clone(),
101 secondary_selections: editor.secondary_selections.clone(),
102 viewport_width: editor.viewport_width,
103 wrap_mode: editor.layout_engine.wrap_mode(),
104 wrap_indent: editor.layout_engine.wrap_indent(),
105 tab_width: editor.layout_engine.tab_width(),
106 tab_key_behavior: executor.tab_key_behavior(),
107 preferred_x_cells: executor.preferred_x_cells(),
108 }
109 }
110
111 fn apply_to_executor(&self, executor: &mut CommandExecutor) {
112 let mut invalidate_visual_rows = false;
113 let editor = executor.editor_mut();
114 editor.cursor_position = self.cursor_position;
115 editor.selection = self.selection.clone();
116 editor.secondary_selections = self.secondary_selections.clone();
117
118 if editor.viewport_width != self.viewport_width {
119 editor.viewport_width = self.viewport_width;
120 invalidate_visual_rows = true;
121 }
122
123 let before_wrap_mode = editor.layout_engine.wrap_mode();
124 let before_wrap_indent = editor.layout_engine.wrap_indent();
125 let before_tab_width = editor.layout_engine.tab_width();
126 let before_viewport_width = editor.layout_engine.viewport_width();
127 editor.layout_engine.set_viewport_width(self.viewport_width);
128 editor.layout_engine.set_wrap_mode(self.wrap_mode);
129 editor.layout_engine.set_wrap_indent(self.wrap_indent);
130 editor.layout_engine.set_tab_width(self.tab_width);
131 if before_wrap_mode != self.wrap_mode
132 || before_wrap_indent != self.wrap_indent
133 || before_tab_width != self.tab_width
134 || before_viewport_width != self.viewport_width
135 {
136 invalidate_visual_rows = true;
137 }
138
139 if invalidate_visual_rows {
140 editor.invalidate_visual_row_index_cache();
141 }
142
143 executor.set_tab_key_behavior(self.tab_key_behavior);
144 executor.set_preferred_x_cells(self.preferred_x_cells);
145 }
146}
147
148struct BufferEntry {
149 meta: BufferMetadata,
150 executor: CommandExecutor,
151 version: u64,
152 last_text_delta: Option<Arc<TextDelta>>,
153}
154
155struct ViewEntry {
156 buffer: BufferId,
157 core: ViewCore,
158 version: u64,
159 callbacks: Vec<StateChangeCallback>,
160 scroll_top: usize,
161 scroll_sub_row_offset: u16,
162 overscan_rows: usize,
163 viewport_height: Option<usize>,
164 last_text_delta: Option<Arc<TextDelta>>,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
169pub enum WorkspaceError {
170 UriAlreadyOpen(String),
172 BufferNotFound(BufferId),
174 ViewNotFound(ViewId),
176 CommandFailed {
178 view: ViewId,
180 message: String,
182 },
183 ApplyEditsFailed {
185 buffer: BufferId,
187 message: String,
189 },
190}
191
192#[derive(Debug, Clone, PartialEq, Eq)]
194pub struct WorkspaceSearchResult {
195 pub id: BufferId,
197 pub uri: Option<String>,
199 pub matches: Vec<SearchMatch>,
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
205pub struct ViewSmoothScrollState {
206 pub top_visual_row: usize,
208 pub sub_row_offset: u16,
210 pub overscan_rows: usize,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct WorkspaceViewportState {
217 pub width: usize,
219 pub height: Option<usize>,
221 pub scroll_top: usize,
223 pub visible_lines: Range<usize>,
225 pub total_visual_lines: usize,
227 pub smooth_scroll: ViewSmoothScrollState,
229 pub prefetch_lines: Range<usize>,
231}
232
233fn apply_char_offset_delta(mut offset: usize, delta: &TextDelta) -> usize {
234 for edit in &delta.edits {
235 let start = edit.start;
236 let end = edit.end();
237 let deleted_len = edit.deleted_len();
238 let inserted_len = edit.inserted_len();
239
240 if offset < start {
241 continue;
242 }
243
244 if offset < end {
245 offset = start.saturating_add(inserted_len);
247 continue;
248 }
249
250 if inserted_len >= deleted_len {
252 offset = offset.saturating_add(inserted_len - deleted_len);
253 } else {
254 offset = offset.saturating_sub(deleted_len - inserted_len);
255 }
256 }
257
258 offset
259}
260
261fn apply_position_delta(
262 old_index: &LineIndex,
263 new_index: &LineIndex,
264 pos: Position,
265 delta: &TextDelta,
266) -> Position {
267 let before = old_index.position_to_char_offset(pos.line, pos.column);
268 let after = apply_char_offset_delta(before, delta);
269 let (line, column) = new_index.char_offset_to_position(after);
270 Position::new(line, column)
271}
272
273fn apply_selection_delta(
274 old_index: &LineIndex,
275 new_index: &LineIndex,
276 selection: &Selection,
277 delta: &TextDelta,
278) -> Selection {
279 let start = apply_position_delta(old_index, new_index, selection.start, delta);
280 let end = apply_position_delta(old_index, new_index, selection.end, delta);
281 Selection {
282 start,
283 end,
284 direction: selection_direction(start, end),
285 }
286}
287
288#[derive(Default)]
290pub struct Workspace {
291 next_buffer_id: u64,
292 buffers: BTreeMap<BufferId, BufferEntry>,
293 uri_to_buffer: HashMap<String, BufferId>,
294
295 next_view_id: u64,
296 views: BTreeMap<ViewId, ViewEntry>,
297 active_view: Option<ViewId>,
298}
299
300impl std::fmt::Debug for Workspace {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 f.debug_struct("Workspace")
303 .field("buffer_count", &self.buffers.len())
304 .field("view_count", &self.views.len())
305 .field("uri_count", &self.uri_to_buffer.len())
306 .field("active_view", &self.active_view)
307 .finish()
308 }
309}
310
311impl Workspace {
312 pub fn new() -> Self {
314 Self::default()
315 }
316
317 pub fn len(&self) -> usize {
319 self.buffers.len()
320 }
321
322 pub fn is_empty(&self) -> bool {
324 self.buffers.is_empty()
325 }
326
327 pub fn view_count(&self) -> usize {
329 self.views.len()
330 }
331
332 pub fn active_view_id(&self) -> Option<ViewId> {
334 self.active_view
335 }
336
337 pub fn active_buffer_id(&self) -> Option<BufferId> {
339 let view_id = self.active_view?;
340 self.views.get(&view_id).map(|v| v.buffer)
341 }
342
343 pub fn set_active_view(&mut self, id: ViewId) -> Result<(), WorkspaceError> {
345 if !self.views.contains_key(&id) {
346 return Err(WorkspaceError::ViewNotFound(id));
347 }
348 self.active_view = Some(id);
349 Ok(())
350 }
351
352 pub fn open_buffer(
358 &mut self,
359 uri: Option<String>,
360 text: &str,
361 viewport_width: usize,
362 ) -> Result<OpenBufferResult, WorkspaceError> {
363 if let Some(uri) = uri.as_ref()
364 && self.uri_to_buffer.contains_key(uri)
365 {
366 return Err(WorkspaceError::UriAlreadyOpen(uri.clone()));
367 }
368
369 let buffer_id = BufferId(self.next_buffer_id);
370 self.next_buffer_id = self.next_buffer_id.saturating_add(1);
371
372 let executor = CommandExecutor::new(text, viewport_width);
373 let meta = BufferMetadata { uri: uri.clone() };
374 self.buffers.insert(
375 buffer_id,
376 BufferEntry {
377 meta,
378 executor,
379 version: 0,
380 last_text_delta: None,
381 },
382 );
383
384 if let Some(uri) = uri {
385 self.uri_to_buffer.insert(uri, buffer_id);
386 }
387
388 let view_id = self.create_view(buffer_id, viewport_width)?;
389
390 if self.active_view.is_none() {
391 self.active_view = Some(view_id);
392 }
393
394 Ok(OpenBufferResult { buffer_id, view_id })
395 }
396
397 pub fn close_buffer(&mut self, id: BufferId) -> Result<(), WorkspaceError> {
399 let Some(entry) = self.buffers.remove(&id) else {
400 return Err(WorkspaceError::BufferNotFound(id));
401 };
402
403 if let Some(uri) = entry.meta.uri.as_ref() {
404 self.uri_to_buffer.remove(uri);
405 }
406
407 let views_to_remove: Vec<ViewId> = self
408 .views
409 .iter()
410 .filter_map(|(vid, v)| if v.buffer == id { Some(*vid) } else { None })
411 .collect();
412 for view_id in views_to_remove {
413 self.views.remove(&view_id);
414 }
415
416 if self
417 .active_view
418 .is_some_and(|active| !self.views.contains_key(&active))
419 {
420 self.active_view = self.views.keys().next().copied();
421 }
422
423 Ok(())
424 }
425
426 pub fn close_view(&mut self, id: ViewId) -> Result<(), WorkspaceError> {
428 let Some(view) = self.views.remove(&id) else {
429 return Err(WorkspaceError::ViewNotFound(id));
430 };
431
432 if self.active_view == Some(id) {
433 self.active_view = self.views.keys().next().copied();
434 }
435
436 let still_has_views = self.views.values().any(|v| v.buffer == view.buffer);
437 if !still_has_views {
438 self.close_buffer(view.buffer)?;
439 }
440
441 Ok(())
442 }
443
444 pub fn create_view(
446 &mut self,
447 buffer: BufferId,
448 viewport_width: usize,
449 ) -> Result<ViewId, WorkspaceError> {
450 let Some(buffer_entry) = self.buffers.get_mut(&buffer) else {
451 return Err(WorkspaceError::BufferNotFound(buffer));
452 };
453
454 let mut core = ViewCore::from_executor(&buffer_entry.executor);
457 core.cursor_position = Position::new(0, 0);
458 core.selection = None;
459 core.secondary_selections.clear();
460 core.viewport_width = viewport_width.max(1);
461 core.preferred_x_cells = None;
462
463 let view_id = ViewId(self.next_view_id);
464 self.next_view_id = self.next_view_id.saturating_add(1);
465
466 self.views.insert(
467 view_id,
468 ViewEntry {
469 buffer,
470 core,
471 version: 0,
472 callbacks: Vec::new(),
473 scroll_top: 0,
474 scroll_sub_row_offset: 0,
475 overscan_rows: 0,
476 viewport_height: None,
477 last_text_delta: None,
478 },
479 );
480
481 Ok(view_id)
482 }
483
484 pub fn buffer_id_for_uri(&self, uri: &str) -> Option<BufferId> {
486 self.uri_to_buffer.get(uri).copied()
487 }
488
489 pub fn buffer_metadata(&self, id: BufferId) -> Option<&BufferMetadata> {
491 self.buffers.get(&id).map(|e| &e.meta)
492 }
493
494 pub fn buffer_id_for_view(&self, id: ViewId) -> Result<BufferId, WorkspaceError> {
496 self.views
497 .get(&id)
498 .map(|v| v.buffer)
499 .ok_or(WorkspaceError::ViewNotFound(id))
500 }
501
502 pub fn cursor_position_for_view(&self, id: ViewId) -> Result<Position, WorkspaceError> {
504 self.views
505 .get(&id)
506 .map(|v| v.core.cursor_position)
507 .ok_or(WorkspaceError::ViewNotFound(id))
508 }
509
510 pub fn selection_for_view(&self, id: ViewId) -> Result<Option<Selection>, WorkspaceError> {
512 self.views
513 .get(&id)
514 .map(|v| v.core.selection.clone())
515 .ok_or(WorkspaceError::ViewNotFound(id))
516 }
517
518 pub fn scroll_top_for_view(&self, id: ViewId) -> Result<usize, WorkspaceError> {
520 self.views
521 .get(&id)
522 .map(|v| v.scroll_top)
523 .ok_or(WorkspaceError::ViewNotFound(id))
524 }
525
526 pub fn scroll_sub_row_offset_for_view(&self, id: ViewId) -> Result<u16, WorkspaceError> {
528 self.views
529 .get(&id)
530 .map(|v| v.scroll_sub_row_offset)
531 .ok_or(WorkspaceError::ViewNotFound(id))
532 }
533
534 pub fn overscan_rows_for_view(&self, id: ViewId) -> Result<usize, WorkspaceError> {
536 self.views
537 .get(&id)
538 .map(|v| v.overscan_rows)
539 .ok_or(WorkspaceError::ViewNotFound(id))
540 }
541
542 pub fn smooth_scroll_state_for_view(
544 &self,
545 id: ViewId,
546 ) -> Result<ViewSmoothScrollState, WorkspaceError> {
547 let Some(view) = self.views.get(&id) else {
548 return Err(WorkspaceError::ViewNotFound(id));
549 };
550 Ok(ViewSmoothScrollState {
551 top_visual_row: view.scroll_top,
552 sub_row_offset: view.scroll_sub_row_offset,
553 overscan_rows: view.overscan_rows,
554 })
555 }
556
557 pub fn set_buffer_uri(
559 &mut self,
560 id: BufferId,
561 uri: Option<String>,
562 ) -> Result<(), WorkspaceError> {
563 let Some(entry) = self.buffers.get_mut(&id) else {
564 return Err(WorkspaceError::BufferNotFound(id));
565 };
566
567 if let Some(next) = uri.as_ref()
568 && self.uri_to_buffer.contains_key(next)
569 && entry.meta.uri.as_deref() != Some(next.as_str())
570 {
571 return Err(WorkspaceError::UriAlreadyOpen(next.clone()));
572 }
573
574 if let Some(prev) = entry.meta.uri.take() {
575 self.uri_to_buffer.remove(&prev);
576 }
577
578 if let Some(next) = uri.clone() {
579 self.uri_to_buffer.insert(next, id);
580 }
581
582 entry.meta.uri = uri;
583 Ok(())
584 }
585
586 pub fn view_version(&self, id: ViewId) -> Option<u64> {
588 self.views.get(&id).map(|v| v.version)
589 }
590
591 pub fn last_text_delta_for_view(&self, id: ViewId) -> Option<&Arc<TextDelta>> {
593 self.views.get(&id)?.last_text_delta.as_ref()
594 }
595
596 pub fn take_last_text_delta_for_view(&mut self, id: ViewId) -> Option<Arc<TextDelta>> {
598 self.views.get_mut(&id)?.last_text_delta.take()
599 }
600
601 pub fn take_last_text_delta_for_buffer(
606 &mut self,
607 id: BufferId,
608 ) -> Result<Option<Arc<TextDelta>>, WorkspaceError> {
609 let Some(buffer) = self.buffers.get_mut(&id) else {
610 return Err(WorkspaceError::BufferNotFound(id));
611 };
612 Ok(buffer.last_text_delta.take())
613 }
614
615 pub fn subscribe_view<F>(&mut self, id: ViewId, callback: F) -> Result<(), WorkspaceError>
617 where
618 F: FnMut(&StateChange) + Send + 'static,
619 {
620 let Some(view) = self.views.get_mut(&id) else {
621 return Err(WorkspaceError::ViewNotFound(id));
622 };
623
624 view.callbacks.push(Box::new(callback));
625 Ok(())
626 }
627
628 fn notify_view(
629 view: &mut ViewEntry,
630 change_type: StateChangeType,
631 delta: Option<Arc<TextDelta>>,
632 ) {
633 let old_version = view.version;
634 view.version = view.version.saturating_add(1);
635
636 let mut change = StateChange::new(change_type, old_version, view.version);
637 if let Some(delta) = delta {
638 change = change.with_text_delta(delta);
639 }
640
641 for cb in &mut view.callbacks {
642 cb(&change);
643 }
644 }
645
646 fn command_change_type(command: &Command) -> Option<StateChangeType> {
647 match command {
648 Command::Edit(EditCommand::InsertText { text }) if text.is_empty() => None,
649 Command::Edit(EditCommand::Delete { length: 0, .. }) => None,
650 Command::Edit(EditCommand::Replace {
651 length: 0, text, ..
652 }) if text.is_empty() => None,
653 Command::Edit(EditCommand::EndUndoGroup) => None,
654 Command::Edit(_) => Some(StateChangeType::DocumentModified),
655 Command::Cursor(
656 CursorCommand::MoveTo { .. }
657 | CursorCommand::MoveBy { .. }
658 | CursorCommand::MoveVisualBy { .. }
659 | CursorCommand::MoveToVisual { .. }
660 | CursorCommand::MoveToLineStart
661 | CursorCommand::MoveToLineEnd
662 | CursorCommand::MoveToVisualLineStart
663 | CursorCommand::MoveToVisualLineEnd
664 | CursorCommand::MoveGraphemeLeft
665 | CursorCommand::MoveGraphemeRight
666 | CursorCommand::MoveWordLeft
667 | CursorCommand::MoveWordRight
668 | CursorCommand::FindNext { .. }
669 | CursorCommand::FindPrev { .. },
670 ) => Some(StateChangeType::CursorMoved),
671 Command::Cursor(_) => Some(StateChangeType::SelectionChanged),
672 Command::View(ViewCommand::ScrollTo { .. } | ViewCommand::GetViewport { .. }) => None,
673 Command::View(_) => Some(StateChangeType::ViewportChanged),
674 Command::Style(
675 crate::StyleCommand::AddStyle { .. } | crate::StyleCommand::RemoveStyle { .. },
676 ) => Some(StateChangeType::StyleChanged),
677 Command::Style(
678 crate::StyleCommand::Fold { .. }
679 | crate::StyleCommand::Unfold { .. }
680 | crate::StyleCommand::UnfoldAll,
681 ) => Some(StateChangeType::FoldingChanged),
682 }
683 }
684
685 pub fn execute(
691 &mut self,
692 view_id: ViewId,
693 command: Command,
694 ) -> Result<CommandResult, WorkspaceError> {
695 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
696 return Err(WorkspaceError::ViewNotFound(view_id));
697 };
698
699 let change_type = Self::command_change_type(&command);
700 if change_type.is_none() {
701 }
703
704 let views = &mut self.views;
706 let buffers = &mut self.buffers;
707
708 let Some(view) = views.get_mut(&view_id) else {
709 return Err(WorkspaceError::ViewNotFound(view_id));
710 };
711 let Some(buffer) = buffers.get_mut(&buffer_id) else {
712 return Err(WorkspaceError::BufferNotFound(buffer_id));
713 };
714
715 let before_view_core = view.core.clone();
716 let before_line_index = buffer.executor.editor().line_index.clone();
717 let before_char_count = buffer.executor.editor().char_count();
718
719 view.core.apply_to_executor(&mut buffer.executor);
721
722 let result = buffer.executor.execute(command.clone()).map_err(|err| {
723 WorkspaceError::CommandFailed {
724 view: view_id,
725 message: err.to_string(),
726 }
727 })?;
728
729 view.core = ViewCore::from_executor(&buffer.executor);
730
731 let delta = buffer.executor.take_last_text_delta().map(Arc::new);
732 let after_char_count = buffer.executor.editor().char_count();
733
734 let view_changed = view.core != before_view_core;
736 let buffer_text_changed = delta.is_some()
737 || after_char_count != before_char_count;
739
740 let buffer_derived_changed = matches!(command, Command::Style(_));
741
742 if !(view_changed || buffer_text_changed || buffer_derived_changed) {
743 return Ok(result);
744 }
745
746 let change_type = if buffer_text_changed {
747 StateChangeType::DocumentModified
748 } else {
749 change_type.unwrap_or(StateChangeType::ViewportChanged)
750 };
751
752 if buffer_text_changed || buffer_derived_changed {
753 let delta_arc = delta.clone();
755 if let Some(delta_arc) = delta_arc {
756 buffer.last_text_delta = Some(delta_arc.clone());
757 for other in views.values_mut() {
758 if other.buffer != buffer_id {
759 continue;
760 }
761 other.last_text_delta = Some(delta_arc.clone());
762 }
763 } else {
764 buffer.last_text_delta = None;
765 }
766
767 if let Some(ref delta_arc) = delta {
769 let new_index = &buffer.executor.editor().line_index;
770 for (other_id, other) in views.iter_mut() {
771 if other.buffer != buffer_id || *other_id == view_id {
772 continue;
773 }
774
775 other.core.cursor_position = apply_position_delta(
776 &before_line_index,
777 new_index,
778 other.core.cursor_position,
779 delta_arc,
780 );
781
782 if let Some(ref sel) = other.core.selection {
783 other.core.selection = Some(apply_selection_delta(
784 &before_line_index,
785 new_index,
786 sel,
787 delta_arc,
788 ));
789 }
790
791 for sel in &mut other.core.secondary_selections {
792 *sel = apply_selection_delta(&before_line_index, new_index, sel, delta_arc);
793 }
794 }
795 }
796
797 for other in views.values_mut() {
798 if other.buffer != buffer_id {
799 continue;
800 }
801 Self::notify_view(other, change_type, delta.clone());
802 }
803
804 buffer.version = buffer.version.saturating_add(1);
805 } else {
806 Self::notify_view(view, change_type, None);
807 }
808
809 Ok(result)
810 }
811
812 pub fn set_viewport_height(
814 &mut self,
815 view_id: ViewId,
816 height: usize,
817 ) -> Result<(), WorkspaceError> {
818 let Some(view) = self.views.get_mut(&view_id) else {
819 return Err(WorkspaceError::ViewNotFound(view_id));
820 };
821 view.viewport_height = Some(height);
822 Ok(())
823 }
824
825 pub fn set_scroll_top(
827 &mut self,
828 view_id: ViewId,
829 scroll_top: usize,
830 ) -> Result<(), WorkspaceError> {
831 let Some(view) = self.views.get_mut(&view_id) else {
832 return Err(WorkspaceError::ViewNotFound(view_id));
833 };
834 view.scroll_top = scroll_top;
835 Ok(())
836 }
837
838 pub fn set_scroll_sub_row_offset(
840 &mut self,
841 view_id: ViewId,
842 sub_row_offset: u16,
843 ) -> Result<(), WorkspaceError> {
844 let Some(view) = self.views.get_mut(&view_id) else {
845 return Err(WorkspaceError::ViewNotFound(view_id));
846 };
847 view.scroll_sub_row_offset = sub_row_offset;
848 Ok(())
849 }
850
851 pub fn set_overscan_rows(
853 &mut self,
854 view_id: ViewId,
855 overscan_rows: usize,
856 ) -> Result<(), WorkspaceError> {
857 let Some(view) = self.views.get_mut(&view_id) else {
858 return Err(WorkspaceError::ViewNotFound(view_id));
859 };
860 view.overscan_rows = overscan_rows;
861 Ok(())
862 }
863
864 pub fn set_smooth_scroll_state(
866 &mut self,
867 view_id: ViewId,
868 state: ViewSmoothScrollState,
869 ) -> Result<(), WorkspaceError> {
870 let Some(view) = self.views.get_mut(&view_id) else {
871 return Err(WorkspaceError::ViewNotFound(view_id));
872 };
873 view.scroll_top = state.top_visual_row;
874 view.scroll_sub_row_offset = state.sub_row_offset;
875 view.overscan_rows = state.overscan_rows;
876 Ok(())
877 }
878
879 pub fn viewport_state_for_view(
881 &mut self,
882 view_id: ViewId,
883 ) -> Result<WorkspaceViewportState, WorkspaceError> {
884 let Some((
885 buffer_id,
886 view_core,
887 scroll_top,
888 viewport_height,
889 sub_row_offset,
890 overscan_rows,
891 )) = self.views.get(&view_id).map(|v| {
892 (
893 v.buffer,
894 v.core.clone(),
895 v.scroll_top,
896 v.viewport_height,
897 v.scroll_sub_row_offset,
898 v.overscan_rows,
899 )
900 })
901 else {
902 return Err(WorkspaceError::ViewNotFound(view_id));
903 };
904
905 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
906 return Err(WorkspaceError::BufferNotFound(buffer_id));
907 };
908 view_core.apply_to_executor(&mut buffer.executor);
909 let editor = buffer.executor.editor();
910
911 let total_visual_lines = editor.visual_line_count();
912 let visible_end = if let Some(height) = viewport_height {
913 scroll_top.saturating_add(height).min(total_visual_lines)
914 } else {
915 total_visual_lines
916 };
917 let visible_lines = scroll_top.min(total_visual_lines)..visible_end;
918 let prefetch_start = visible_lines.start.saturating_sub(overscan_rows);
919 let prefetch_end = visible_lines
920 .end
921 .saturating_add(overscan_rows)
922 .min(total_visual_lines);
923
924 Ok(WorkspaceViewportState {
925 width: editor.viewport_width,
926 height: viewport_height,
927 scroll_top,
928 visible_lines,
929 total_visual_lines,
930 smooth_scroll: ViewSmoothScrollState {
931 top_visual_row: scroll_top,
932 sub_row_offset,
933 overscan_rows,
934 },
935 prefetch_lines: prefetch_start..prefetch_end,
936 })
937 }
938
939 pub fn total_visual_lines_for_view(
941 &mut self,
942 view_id: ViewId,
943 ) -> Result<usize, WorkspaceError> {
944 Ok(self.viewport_state_for_view(view_id)?.total_visual_lines)
945 }
946
947 pub fn visual_to_logical_for_view(
949 &mut self,
950 view_id: ViewId,
951 visual_row: usize,
952 ) -> Result<(usize, usize), WorkspaceError> {
953 let Some((buffer_id, view_core)) =
954 self.views.get(&view_id).map(|v| (v.buffer, v.core.clone()))
955 else {
956 return Err(WorkspaceError::ViewNotFound(view_id));
957 };
958 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
959 return Err(WorkspaceError::BufferNotFound(buffer_id));
960 };
961 view_core.apply_to_executor(&mut buffer.executor);
962 Ok(buffer.executor.editor().visual_to_logical_line(visual_row))
963 }
964
965 pub fn logical_to_visual_for_view(
967 &mut self,
968 view_id: ViewId,
969 line: usize,
970 column: usize,
971 ) -> Result<Option<(usize, usize)>, WorkspaceError> {
972 let Some((buffer_id, view_core)) =
973 self.views.get(&view_id).map(|v| (v.buffer, v.core.clone()))
974 else {
975 return Err(WorkspaceError::ViewNotFound(view_id));
976 };
977 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
978 return Err(WorkspaceError::BufferNotFound(buffer_id));
979 };
980 view_core.apply_to_executor(&mut buffer.executor);
981 Ok(buffer
982 .executor
983 .editor()
984 .logical_position_to_visual(line, column))
985 }
986
987 pub fn visual_position_to_logical_for_view(
989 &mut self,
990 view_id: ViewId,
991 visual_row: usize,
992 x_cells: usize,
993 ) -> Result<Option<Position>, WorkspaceError> {
994 let Some((buffer_id, view_core)) =
995 self.views.get(&view_id).map(|v| (v.buffer, v.core.clone()))
996 else {
997 return Err(WorkspaceError::ViewNotFound(view_id));
998 };
999 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1000 return Err(WorkspaceError::BufferNotFound(buffer_id));
1001 };
1002 view_core.apply_to_executor(&mut buffer.executor);
1003 Ok(buffer
1004 .executor
1005 .editor()
1006 .visual_position_to_logical(visual_row, x_cells))
1007 }
1008
1009 pub fn buffer_text(&self, buffer_id: BufferId) -> Result<String, WorkspaceError> {
1011 let Some(buffer) = self.buffers.get(&buffer_id) else {
1012 return Err(WorkspaceError::BufferNotFound(buffer_id));
1013 };
1014 Ok(buffer.executor.editor().get_text())
1015 }
1016
1017 pub fn get_viewport_content_styled(
1019 &mut self,
1020 view_id: ViewId,
1021 start_visual_row: usize,
1022 count: usize,
1023 ) -> Result<crate::HeadlessGrid, WorkspaceError> {
1024 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1025 return Err(WorkspaceError::ViewNotFound(view_id));
1026 };
1027
1028 let view_core = self
1029 .views
1030 .get(&view_id)
1031 .map(|v| v.core.clone())
1032 .ok_or(WorkspaceError::ViewNotFound(view_id))?;
1033
1034 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1035 return Err(WorkspaceError::BufferNotFound(buffer_id));
1036 };
1037
1038 view_core.apply_to_executor(&mut buffer.executor);
1039 Ok(buffer
1040 .executor
1041 .editor()
1042 .get_headless_grid_styled(start_visual_row, count))
1043 }
1044
1045 pub fn get_minimap_content(
1047 &mut self,
1048 view_id: ViewId,
1049 start_visual_row: usize,
1050 count: usize,
1051 ) -> Result<crate::MinimapGrid, WorkspaceError> {
1052 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1053 return Err(WorkspaceError::ViewNotFound(view_id));
1054 };
1055
1056 let view_core = self
1057 .views
1058 .get(&view_id)
1059 .map(|v| v.core.clone())
1060 .ok_or(WorkspaceError::ViewNotFound(view_id))?;
1061
1062 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1063 return Err(WorkspaceError::BufferNotFound(buffer_id));
1064 };
1065
1066 view_core.apply_to_executor(&mut buffer.executor);
1067 Ok(buffer
1068 .executor
1069 .editor()
1070 .get_minimap_grid(start_visual_row, count))
1071 }
1072
1073 pub fn get_viewport_content_composed(
1078 &mut self,
1079 view_id: ViewId,
1080 start_visual_row: usize,
1081 count: usize,
1082 ) -> Result<crate::ComposedGrid, WorkspaceError> {
1083 let Some(buffer_id) = self.views.get(&view_id).map(|v| v.buffer) else {
1084 return Err(WorkspaceError::ViewNotFound(view_id));
1085 };
1086
1087 let view_core = self
1088 .views
1089 .get(&view_id)
1090 .map(|v| v.core.clone())
1091 .ok_or(WorkspaceError::ViewNotFound(view_id))?;
1092
1093 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1094 return Err(WorkspaceError::BufferNotFound(buffer_id));
1095 };
1096
1097 view_core.apply_to_executor(&mut buffer.executor);
1098 Ok(buffer
1099 .executor
1100 .editor()
1101 .get_headless_grid_composed(start_visual_row, count))
1102 }
1103
1104 pub fn apply_processing_edits<I>(
1106 &mut self,
1107 buffer_id: BufferId,
1108 edits: I,
1109 ) -> Result<(), WorkspaceError>
1110 where
1111 I: IntoIterator<Item = ProcessingEdit>,
1112 {
1113 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1114 return Err(WorkspaceError::BufferNotFound(buffer_id));
1115 };
1116
1117 let mut style_changed = false;
1118 let mut folding_changed = false;
1119 let mut diagnostics_changed = false;
1120 let mut decorations_changed = false;
1121 let mut symbols_changed = false;
1122
1123 for edit in edits {
1124 match edit {
1125 ProcessingEdit::ReplaceStyleLayer { layer, intervals } => {
1126 let editor = buffer.executor.editor_mut();
1127 if intervals.is_empty() {
1128 editor.style_layers.remove(&layer);
1129 } else {
1130 let tree = editor.style_layers.entry(layer).or_default();
1131 tree.clear();
1132 for interval in intervals {
1133 if interval.start < interval.end {
1134 tree.insert(interval);
1135 }
1136 }
1137 }
1138 style_changed = true;
1139 }
1140 ProcessingEdit::ClearStyleLayer { layer } => {
1141 buffer.executor.editor_mut().style_layers.remove(&layer);
1142 style_changed = true;
1143 }
1144 ProcessingEdit::ReplaceFoldingRegions {
1145 mut regions,
1146 preserve_collapsed,
1147 } => {
1148 if preserve_collapsed {
1149 let collapsed: HashSet<(usize, usize)> = buffer
1150 .executor
1151 .editor()
1152 .folding_manager
1153 .derived_regions()
1154 .iter()
1155 .filter(|r| r.is_collapsed)
1156 .map(|r| (r.start_line, r.end_line))
1157 .collect();
1158
1159 for region in &mut regions {
1160 if collapsed.contains(&(region.start_line, region.end_line)) {
1161 region.is_collapsed = true;
1162 }
1163 }
1164 }
1165
1166 buffer
1167 .executor
1168 .editor_mut()
1169 .folding_manager
1170 .replace_derived_regions(regions);
1171 buffer
1172 .executor
1173 .editor_mut()
1174 .invalidate_visual_row_index_cache();
1175 folding_changed = true;
1176 }
1177 ProcessingEdit::ClearFoldingRegions => {
1178 buffer
1179 .executor
1180 .editor_mut()
1181 .folding_manager
1182 .clear_derived_regions();
1183 buffer
1184 .executor
1185 .editor_mut()
1186 .invalidate_visual_row_index_cache();
1187 folding_changed = true;
1188 }
1189 ProcessingEdit::ReplaceDiagnostics { diagnostics } => {
1190 buffer.executor.editor_mut().diagnostics = diagnostics;
1191 diagnostics_changed = true;
1192 }
1193 ProcessingEdit::ClearDiagnostics => {
1194 buffer.executor.editor_mut().diagnostics.clear();
1195 diagnostics_changed = true;
1196 }
1197 ProcessingEdit::ReplaceDecorations {
1198 layer,
1199 mut decorations,
1200 } => {
1201 decorations.sort_unstable_by_key(|d| (d.range.start, d.range.end));
1202 buffer
1203 .executor
1204 .editor_mut()
1205 .decorations
1206 .insert(layer, decorations);
1207 decorations_changed = true;
1208 }
1209 ProcessingEdit::ClearDecorations { layer } => {
1210 buffer.executor.editor_mut().decorations.remove(&layer);
1211 decorations_changed = true;
1212 }
1213 ProcessingEdit::ReplaceDocumentSymbols { symbols } => {
1214 buffer.executor.editor_mut().document_symbols = symbols;
1215 symbols_changed = true;
1216 }
1217 ProcessingEdit::ClearDocumentSymbols => {
1218 buffer.executor.editor_mut().document_symbols =
1219 crate::DocumentOutline::default();
1220 symbols_changed = true;
1221 }
1222 }
1223 }
1224
1225 let change_type = if folding_changed {
1226 Some(StateChangeType::FoldingChanged)
1227 } else if style_changed {
1228 Some(StateChangeType::StyleChanged)
1229 } else if decorations_changed {
1230 Some(StateChangeType::DecorationsChanged)
1231 } else if diagnostics_changed {
1232 Some(StateChangeType::DiagnosticsChanged)
1233 } else if symbols_changed {
1234 Some(StateChangeType::SymbolsChanged)
1235 } else {
1236 None
1237 };
1238
1239 if let Some(change_type) = change_type {
1240 for view in self.views.values_mut() {
1241 if view.buffer == buffer_id {
1242 Self::notify_view(view, change_type, None);
1243 }
1244 }
1245 buffer.version = buffer.version.saturating_add(1);
1246 }
1247
1248 Ok(())
1249 }
1250
1251 pub fn search_all_open_buffers(
1256 &self,
1257 query: &str,
1258 options: SearchOptions,
1259 ) -> Result<Vec<WorkspaceSearchResult>, SearchError> {
1260 let mut out: Vec<WorkspaceSearchResult> = Vec::new();
1261
1262 for (id, entry) in &self.buffers {
1263 let text = entry.executor.editor().get_text();
1264 let matches = find_all(&text, query, options)?;
1265 if matches.is_empty() {
1266 continue;
1267 }
1268
1269 out.push(WorkspaceSearchResult {
1270 id: *id,
1271 uri: entry.meta.uri.clone(),
1272 matches,
1273 });
1274 }
1275
1276 Ok(out)
1277 }
1278
1279 pub fn apply_text_edits<I>(
1285 &mut self,
1286 edits: I,
1287 ) -> Result<Vec<(BufferId, usize)>, WorkspaceError>
1288 where
1289 I: IntoIterator<Item = (BufferId, Vec<TextEditSpec>)>,
1290 {
1291 let mut by_id: BTreeMap<BufferId, Vec<TextEditSpec>> = BTreeMap::new();
1292 for (id, mut buffer_edits) in edits {
1293 by_id.entry(id).or_default().append(&mut buffer_edits);
1294 }
1295
1296 let mut applied: Vec<(BufferId, usize)> = Vec::new();
1297 for (buffer_id, buffer_edits) in by_id {
1298 let edit_count = buffer_edits.len();
1299 if edit_count == 0 {
1300 continue;
1301 }
1302
1303 let Some(buffer) = self.buffers.get_mut(&buffer_id) else {
1304 return Err(WorkspaceError::BufferNotFound(buffer_id));
1305 };
1306
1307 let before_line_index = buffer.executor.editor().line_index.clone();
1308 let before_char_count = buffer.executor.editor().char_count();
1309
1310 let neutral = ViewCore {
1312 cursor_position: Position::new(0, 0),
1313 selection: None,
1314 secondary_selections: Vec::new(),
1315 viewport_width: buffer.executor.editor().viewport_width.max(1),
1316 wrap_mode: buffer.executor.editor().layout_engine.wrap_mode(),
1317 wrap_indent: buffer.executor.editor().layout_engine.wrap_indent(),
1318 tab_width: buffer.executor.editor().layout_engine.tab_width(),
1319 tab_key_behavior: buffer.executor.tab_key_behavior(),
1320 preferred_x_cells: None,
1321 };
1322 neutral.apply_to_executor(&mut buffer.executor);
1323
1324 buffer
1325 .executor
1326 .execute(Command::Edit(EditCommand::ApplyTextEdits {
1327 edits: buffer_edits,
1328 }))
1329 .map_err(|err| WorkspaceError::ApplyEditsFailed {
1330 buffer: buffer_id,
1331 message: err.to_string(),
1332 })?;
1333
1334 let delta = buffer.executor.take_last_text_delta().map(Arc::new);
1335 let after_char_count = buffer.executor.editor().char_count();
1336 let changed = delta.is_some() || after_char_count != before_char_count;
1337
1338 if changed {
1339 if let Some(ref delta_arc) = delta {
1340 buffer.last_text_delta = Some(delta_arc.clone());
1341 let new_index = &buffer.executor.editor().line_index;
1342 for view in self.views.values_mut() {
1343 if view.buffer != buffer_id {
1344 continue;
1345 }
1346
1347 view.last_text_delta = Some(delta_arc.clone());
1348
1349 view.core.cursor_position = apply_position_delta(
1350 &before_line_index,
1351 new_index,
1352 view.core.cursor_position,
1353 delta_arc,
1354 );
1355 if let Some(ref sel) = view.core.selection {
1356 view.core.selection = Some(apply_selection_delta(
1357 &before_line_index,
1358 new_index,
1359 sel,
1360 delta_arc,
1361 ));
1362 }
1363 for sel in &mut view.core.secondary_selections {
1364 *sel = apply_selection_delta(
1365 &before_line_index,
1366 new_index,
1367 sel,
1368 delta_arc,
1369 );
1370 }
1371
1372 Self::notify_view(
1373 view,
1374 StateChangeType::DocumentModified,
1375 Some(delta_arc.clone()),
1376 );
1377 }
1378 } else {
1379 buffer.last_text_delta = None;
1380 for view in self.views.values_mut() {
1381 if view.buffer == buffer_id {
1382 Self::notify_view(view, StateChangeType::DocumentModified, None);
1383 }
1384 }
1385 }
1386
1387 buffer.version = buffer.version.saturating_add(1);
1388 }
1389
1390 applied.push((buffer_id, edit_count));
1391 }
1392
1393 Ok(applied)
1394 }
1395}