1mod blink_manager;
2pub mod display_map;
3mod editor_settings;
4mod element;
5
6mod git;
7mod highlight_matching_bracket;
8mod hover_popover;
9pub mod items;
10mod link_go_to_definition;
11mod mouse_context_menu;
12pub mod movement;
13pub mod multi_buffer;
14mod persistence;
15pub mod scroll;
16pub mod selections_collection;
17
18#[cfg(test)]
19mod editor_tests;
20#[cfg(any(test, feature = "test-support"))]
21pub mod test;
22
23use ::git::diff::DiffHunk;
24use aho_corasick::AhoCorasick;
25use anyhow::{anyhow, Result};
26use blink_manager::BlinkManager;
27use client::{ClickhouseEvent, TelemetrySettings};
28use clock::ReplicaId;
29use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
30use copilot::Copilot;
31pub use display_map::DisplayPoint;
32use display_map::*;
33pub use editor_settings::EditorSettings;
34pub use element::{
35 Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
36};
37use futures::FutureExt;
38use fuzzy::{StringMatch, StringMatchCandidate};
39use gpui::{
40 actions,
41 color::Color,
42 elements::*,
43 executor,
44 fonts::{self, HighlightStyle, TextStyle},
45 geometry::vector::Vector2F,
46 impl_actions,
47 keymap_matcher::KeymapContext,
48 platform::{CursorStyle, MouseButton},
49 serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element,
50 Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
51 WindowContext,
52};
53use highlight_matching_bracket::refresh_matching_bracket_highlights;
54use hover_popover::{hide_hover, HoverState};
55pub use items::MAX_TAB_TITLE_LEN;
56use itertools::Itertools;
57pub use language::{char_kind, CharKind};
58use language::{
59 language_settings::{self, all_language_settings},
60 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
61 Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
62 OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
63};
64use link_go_to_definition::{
65 hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
66};
67pub use multi_buffer::{
68 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
69 ToPoint,
70};
71use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
72use ordered_float::OrderedFloat;
73use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
74use scroll::{
75 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
76};
77use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
78use serde::{Deserialize, Serialize};
79use settings::SettingsStore;
80use smallvec::SmallVec;
81use snippet::Snippet;
82use std::{
83 any::TypeId,
84 borrow::Cow,
85 cmp::{self, Ordering, Reverse},
86 mem,
87 num::NonZeroU32,
88 ops::{Deref, DerefMut, Range},
89 path::Path,
90 sync::Arc,
91 time::{Duration, Instant},
92};
93pub use sum_tree::Bias;
94use theme::{DiagnosticStyle, Theme, ThemeSettings};
95use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
96use workspace::{ItemNavHistory, ViewId, Workspace};
97
98use crate::git::diff_hunk_to_display;
99
100const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
101const MAX_LINE_LEN: usize = 1024;
102const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
103const MAX_SELECTION_HISTORY_LEN: usize = 1024;
104const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
105
106pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
107
108#[derive(Clone, Deserialize, PartialEq, Default)]
109pub struct SelectNext {
110 #[serde(default)]
111 pub replace_newest: bool,
112}
113
114#[derive(Clone, Deserialize, PartialEq, Default)]
115pub struct SelectPrevious {
116 #[serde(default)]
117 pub replace_newest: bool,
118}
119
120#[derive(Clone, Deserialize, PartialEq)]
121pub struct SelectToBeginningOfLine {
122 #[serde(default)]
123 stop_at_soft_wraps: bool,
124}
125
126#[derive(Clone, Default, Deserialize, PartialEq)]
127pub struct MovePageUp {
128 #[serde(default)]
129 center_cursor: bool,
130}
131
132#[derive(Clone, Default, Deserialize, PartialEq)]
133pub struct MovePageDown {
134 #[serde(default)]
135 center_cursor: bool,
136}
137
138#[derive(Clone, Deserialize, PartialEq)]
139pub struct SelectToEndOfLine {
140 #[serde(default)]
141 stop_at_soft_wraps: bool,
142}
143
144#[derive(Clone, Deserialize, PartialEq)]
145pub struct ToggleCodeActions {
146 #[serde(default)]
147 pub deployed_from_indicator: bool,
148}
149
150#[derive(Clone, Default, Deserialize, PartialEq)]
151pub struct ConfirmCompletion {
152 #[serde(default)]
153 pub item_ix: Option<usize>,
154}
155
156#[derive(Clone, Default, Deserialize, PartialEq)]
157pub struct ConfirmCodeAction {
158 #[serde(default)]
159 pub item_ix: Option<usize>,
160}
161
162#[derive(Clone, Default, Deserialize, PartialEq)]
163pub struct ToggleComments {
164 #[serde(default)]
165 pub advance_downwards: bool,
166}
167
168#[derive(Clone, Default, Deserialize, PartialEq)]
169pub struct FoldAt {
170 pub buffer_row: u32,
171}
172
173#[derive(Clone, Default, Deserialize, PartialEq)]
174pub struct UnfoldAt {
175 pub buffer_row: u32,
176}
177
178#[derive(Clone, Default, Deserialize, PartialEq)]
179pub struct GutterHover {
180 pub hovered: bool,
181}
182
183actions!(
184 editor,
185 [
186 Cancel,
187 Backspace,
188 Delete,
189 Newline,
190 NewlineAbove,
191 NewlineBelow,
192 GoToDiagnostic,
193 GoToPrevDiagnostic,
194 GoToHunk,
195 GoToPrevHunk,
196 Indent,
197 Outdent,
198 DeleteLine,
199 DeleteToPreviousWordStart,
200 DeleteToPreviousSubwordStart,
201 DeleteToNextWordEnd,
202 DeleteToNextSubwordEnd,
203 DeleteToBeginningOfLine,
204 DeleteToEndOfLine,
205 CutToEndOfLine,
206 DuplicateLine,
207 MoveLineUp,
208 MoveLineDown,
209 JoinLines,
210 Transpose,
211 Cut,
212 Copy,
213 Paste,
214 Undo,
215 Redo,
216 MoveUp,
217 PageUp,
218 MoveDown,
219 PageDown,
220 MoveLeft,
221 MoveRight,
222 MoveToPreviousWordStart,
223 MoveToPreviousSubwordStart,
224 MoveToNextWordEnd,
225 MoveToNextSubwordEnd,
226 MoveToBeginningOfLine,
227 MoveToEndOfLine,
228 MoveToStartOfParagraph,
229 MoveToEndOfParagraph,
230 MoveToBeginning,
231 MoveToEnd,
232 SelectUp,
233 SelectDown,
234 SelectLeft,
235 SelectRight,
236 SelectToPreviousWordStart,
237 SelectToPreviousSubwordStart,
238 SelectToNextWordEnd,
239 SelectToNextSubwordEnd,
240 SelectToStartOfParagraph,
241 SelectToEndOfParagraph,
242 SelectToBeginning,
243 SelectToEnd,
244 SelectAll,
245 SelectLine,
246 SplitSelectionIntoLines,
247 AddSelectionAbove,
248 AddSelectionBelow,
249 Tab,
250 TabPrev,
251 ShowCharacterPalette,
252 SelectLargerSyntaxNode,
253 SelectSmallerSyntaxNode,
254 GoToDefinition,
255 GoToTypeDefinition,
256 MoveToEnclosingBracket,
257 UndoSelection,
258 RedoSelection,
259 FindAllReferences,
260 Rename,
261 ConfirmRename,
262 Fold,
263 UnfoldLines,
264 FoldSelectedRanges,
265 ShowCompletions,
266 OpenExcerpts,
267 RestartLanguageServer,
268 Hover,
269 Format,
270 ToggleSoftWrap,
271 RevealInFinder,
272 CopyPath,
273 CopyRelativePath,
274 CopyHighlightJson
275 ]
276);
277
278impl_actions!(
279 editor,
280 [
281 SelectNext,
282 SelectPrevious,
283 SelectToBeginningOfLine,
284 SelectToEndOfLine,
285 ToggleCodeActions,
286 MovePageUp,
287 MovePageDown,
288 ConfirmCompletion,
289 ConfirmCodeAction,
290 ToggleComments,
291 FoldAt,
292 UnfoldAt,
293 GutterHover
294 ]
295);
296
297enum DocumentHighlightRead {}
298enum DocumentHighlightWrite {}
299enum InputComposition {}
300
301#[derive(Copy, Clone, PartialEq, Eq)]
302pub enum Direction {
303 Prev,
304 Next,
305}
306
307pub fn init_settings(cx: &mut AppContext) {
308 settings::register::<EditorSettings>(cx);
309}
310
311pub fn init(cx: &mut AppContext) {
312 init_settings(cx);
313 cx.add_action(Editor::new_file);
314 cx.add_action(Editor::cancel);
315 cx.add_action(Editor::newline);
316 cx.add_action(Editor::newline_above);
317 cx.add_action(Editor::newline_below);
318 cx.add_action(Editor::backspace);
319 cx.add_action(Editor::delete);
320 cx.add_action(Editor::tab);
321 cx.add_action(Editor::tab_prev);
322 cx.add_action(Editor::indent);
323 cx.add_action(Editor::outdent);
324 cx.add_action(Editor::delete_line);
325 cx.add_action(Editor::join_lines);
326 cx.add_action(Editor::delete_to_previous_word_start);
327 cx.add_action(Editor::delete_to_previous_subword_start);
328 cx.add_action(Editor::delete_to_next_word_end);
329 cx.add_action(Editor::delete_to_next_subword_end);
330 cx.add_action(Editor::delete_to_beginning_of_line);
331 cx.add_action(Editor::delete_to_end_of_line);
332 cx.add_action(Editor::cut_to_end_of_line);
333 cx.add_action(Editor::duplicate_line);
334 cx.add_action(Editor::move_line_up);
335 cx.add_action(Editor::move_line_down);
336 cx.add_action(Editor::transpose);
337 cx.add_action(Editor::cut);
338 cx.add_action(Editor::copy);
339 cx.add_action(Editor::paste);
340 cx.add_action(Editor::undo);
341 cx.add_action(Editor::redo);
342 cx.add_action(Editor::move_up);
343 cx.add_action(Editor::move_page_up);
344 cx.add_action(Editor::move_down);
345 cx.add_action(Editor::move_page_down);
346 cx.add_action(Editor::next_screen);
347 cx.add_action(Editor::move_left);
348 cx.add_action(Editor::move_right);
349 cx.add_action(Editor::move_to_previous_word_start);
350 cx.add_action(Editor::move_to_previous_subword_start);
351 cx.add_action(Editor::move_to_next_word_end);
352 cx.add_action(Editor::move_to_next_subword_end);
353 cx.add_action(Editor::move_to_beginning_of_line);
354 cx.add_action(Editor::move_to_end_of_line);
355 cx.add_action(Editor::move_to_start_of_paragraph);
356 cx.add_action(Editor::move_to_end_of_paragraph);
357 cx.add_action(Editor::move_to_beginning);
358 cx.add_action(Editor::move_to_end);
359 cx.add_action(Editor::select_up);
360 cx.add_action(Editor::select_down);
361 cx.add_action(Editor::select_left);
362 cx.add_action(Editor::select_right);
363 cx.add_action(Editor::select_to_previous_word_start);
364 cx.add_action(Editor::select_to_previous_subword_start);
365 cx.add_action(Editor::select_to_next_word_end);
366 cx.add_action(Editor::select_to_next_subword_end);
367 cx.add_action(Editor::select_to_beginning_of_line);
368 cx.add_action(Editor::select_to_end_of_line);
369 cx.add_action(Editor::select_to_start_of_paragraph);
370 cx.add_action(Editor::select_to_end_of_paragraph);
371 cx.add_action(Editor::select_to_beginning);
372 cx.add_action(Editor::select_to_end);
373 cx.add_action(Editor::select_all);
374 cx.add_action(Editor::select_line);
375 cx.add_action(Editor::split_selection_into_lines);
376 cx.add_action(Editor::add_selection_above);
377 cx.add_action(Editor::add_selection_below);
378 cx.add_action(Editor::select_next);
379 cx.add_action(Editor::select_previous);
380 cx.add_action(Editor::toggle_comments);
381 cx.add_action(Editor::select_larger_syntax_node);
382 cx.add_action(Editor::select_smaller_syntax_node);
383 cx.add_action(Editor::move_to_enclosing_bracket);
384 cx.add_action(Editor::undo_selection);
385 cx.add_action(Editor::redo_selection);
386 cx.add_action(Editor::go_to_diagnostic);
387 cx.add_action(Editor::go_to_prev_diagnostic);
388 cx.add_action(Editor::go_to_hunk);
389 cx.add_action(Editor::go_to_prev_hunk);
390 cx.add_action(Editor::go_to_definition);
391 cx.add_action(Editor::go_to_type_definition);
392 cx.add_action(Editor::fold);
393 cx.add_action(Editor::fold_at);
394 cx.add_action(Editor::unfold_lines);
395 cx.add_action(Editor::unfold_at);
396 cx.add_action(Editor::gutter_hover);
397 cx.add_action(Editor::fold_selected_ranges);
398 cx.add_action(Editor::show_completions);
399 cx.add_action(Editor::toggle_code_actions);
400 cx.add_action(Editor::open_excerpts);
401 cx.add_action(Editor::toggle_soft_wrap);
402 cx.add_action(Editor::reveal_in_finder);
403 cx.add_action(Editor::copy_path);
404 cx.add_action(Editor::copy_relative_path);
405 cx.add_action(Editor::copy_highlight_json);
406 cx.add_async_action(Editor::format);
407 cx.add_action(Editor::restart_language_server);
408 cx.add_action(Editor::show_character_palette);
409 cx.add_async_action(Editor::confirm_completion);
410 cx.add_async_action(Editor::confirm_code_action);
411 cx.add_async_action(Editor::rename);
412 cx.add_async_action(Editor::confirm_rename);
413 cx.add_async_action(Editor::find_all_references);
414 cx.add_action(Editor::next_copilot_suggestion);
415 cx.add_action(Editor::previous_copilot_suggestion);
416 cx.add_action(Editor::copilot_suggest);
417
418 hover_popover::init(cx);
419 scroll::actions::init(cx);
420
421 workspace::register_project_item::<Editor>(cx);
422 workspace::register_followable_item::<Editor>(cx);
423 workspace::register_deserializable_item::<Editor>(cx);
424}
425
426trait InvalidationRegion {
427 fn ranges(&self) -> &[Range<Anchor>];
428}
429
430#[derive(Clone, Debug, PartialEq)]
431pub enum SelectPhase {
432 Begin {
433 position: DisplayPoint,
434 add: bool,
435 click_count: usize,
436 },
437 BeginColumnar {
438 position: DisplayPoint,
439 goal_column: u32,
440 },
441 Extend {
442 position: DisplayPoint,
443 click_count: usize,
444 },
445 Update {
446 position: DisplayPoint,
447 goal_column: u32,
448 scroll_position: Vector2F,
449 },
450 End,
451}
452
453#[derive(Clone, Debug)]
454pub enum SelectMode {
455 Character,
456 Word(Range<Anchor>),
457 Line(Range<Anchor>),
458 All,
459}
460
461#[derive(Copy, Clone, PartialEq, Eq, Debug)]
462pub enum EditorMode {
463 SingleLine,
464 AutoHeight { max_lines: usize },
465 Full,
466}
467
468#[derive(Clone, Debug)]
469pub enum SoftWrap {
470 None,
471 EditorWidth,
472 Column(u32),
473}
474
475#[derive(Clone)]
476pub struct EditorStyle {
477 pub text: TextStyle,
478 pub placeholder_text: Option<TextStyle>,
479 pub theme: theme::Editor,
480 pub theme_id: usize,
481}
482
483type CompletionId = usize;
484
485type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
486type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
487
488pub struct Editor {
489 handle: WeakViewHandle<Self>,
490 buffer: ModelHandle<MultiBuffer>,
491 display_map: ModelHandle<DisplayMap>,
492 pub selections: SelectionsCollection,
493 pub scroll_manager: ScrollManager,
494 columnar_selection_tail: Option<Anchor>,
495 add_selections_state: Option<AddSelectionsState>,
496 select_next_state: Option<SelectNextState>,
497 select_prev_state: Option<SelectNextState>,
498 selection_history: SelectionHistory,
499 autoclose_regions: Vec<AutocloseRegion>,
500 snippet_stack: InvalidationStack<SnippetState>,
501 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
502 ime_transaction: Option<TransactionId>,
503 active_diagnostics: Option<ActiveDiagnosticGroup>,
504 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
505 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
506 override_text_style: Option<Box<OverrideTextStyle>>,
507 project: Option<ModelHandle<Project>>,
508 focused: bool,
509 blink_manager: ModelHandle<BlinkManager>,
510 show_local_selections: bool,
511 mode: EditorMode,
512 show_gutter: bool,
513 placeholder_text: Option<Arc<str>>,
514 highlighted_rows: Option<Range<u32>>,
515 #[allow(clippy::type_complexity)]
516 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
517 nav_history: Option<ItemNavHistory>,
518 context_menu: Option<ContextMenu>,
519 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
520 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
521 next_completion_id: CompletionId,
522 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
523 code_actions_task: Option<Task<()>>,
524 document_highlights_task: Option<Task<()>>,
525 pending_rename: Option<RenameState>,
526 searchable: bool,
527 cursor_shape: CursorShape,
528 workspace: Option<(WeakViewHandle<Workspace>, i64)>,
529 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
530 input_enabled: bool,
531 read_only: bool,
532 leader_replica_id: Option<u16>,
533 remote_id: Option<ViewId>,
534 hover_state: HoverState,
535 gutter_hovered: bool,
536 link_go_to_definition_state: LinkGoToDefinitionState,
537 copilot_state: CopilotState,
538 _subscriptions: Vec<Subscription>,
539}
540
541pub struct EditorSnapshot {
542 pub mode: EditorMode,
543 pub show_gutter: bool,
544 pub display_snapshot: DisplaySnapshot,
545 pub placeholder_text: Option<Arc<str>>,
546 is_focused: bool,
547 scroll_anchor: ScrollAnchor,
548 ongoing_scroll: OngoingScroll,
549}
550
551#[derive(Clone, Debug)]
552struct SelectionHistoryEntry {
553 selections: Arc<[Selection<Anchor>]>,
554 select_next_state: Option<SelectNextState>,
555 select_prev_state: Option<SelectNextState>,
556 add_selections_state: Option<AddSelectionsState>,
557}
558
559enum SelectionHistoryMode {
560 Normal,
561 Undoing,
562 Redoing,
563}
564
565impl Default for SelectionHistoryMode {
566 fn default() -> Self {
567 Self::Normal
568 }
569}
570
571#[derive(Default)]
572struct SelectionHistory {
573 #[allow(clippy::type_complexity)]
574 selections_by_transaction:
575 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
576 mode: SelectionHistoryMode,
577 undo_stack: VecDeque<SelectionHistoryEntry>,
578 redo_stack: VecDeque<SelectionHistoryEntry>,
579}
580
581impl SelectionHistory {
582 fn insert_transaction(
583 &mut self,
584 transaction_id: TransactionId,
585 selections: Arc<[Selection<Anchor>]>,
586 ) {
587 self.selections_by_transaction
588 .insert(transaction_id, (selections, None));
589 }
590
591 #[allow(clippy::type_complexity)]
592 fn transaction(
593 &self,
594 transaction_id: TransactionId,
595 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
596 self.selections_by_transaction.get(&transaction_id)
597 }
598
599 #[allow(clippy::type_complexity)]
600 fn transaction_mut(
601 &mut self,
602 transaction_id: TransactionId,
603 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
604 self.selections_by_transaction.get_mut(&transaction_id)
605 }
606
607 fn push(&mut self, entry: SelectionHistoryEntry) {
608 if !entry.selections.is_empty() {
609 match self.mode {
610 SelectionHistoryMode::Normal => {
611 self.push_undo(entry);
612 self.redo_stack.clear();
613 }
614 SelectionHistoryMode::Undoing => self.push_redo(entry),
615 SelectionHistoryMode::Redoing => self.push_undo(entry),
616 }
617 }
618 }
619
620 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
621 if self
622 .undo_stack
623 .back()
624 .map_or(true, |e| e.selections != entry.selections)
625 {
626 self.undo_stack.push_back(entry);
627 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
628 self.undo_stack.pop_front();
629 }
630 }
631 }
632
633 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
634 if self
635 .redo_stack
636 .back()
637 .map_or(true, |e| e.selections != entry.selections)
638 {
639 self.redo_stack.push_back(entry);
640 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
641 self.redo_stack.pop_front();
642 }
643 }
644 }
645}
646
647#[derive(Clone, Debug)]
648struct AddSelectionsState {
649 above: bool,
650 stack: Vec<usize>,
651}
652
653#[derive(Clone, Debug)]
654struct SelectNextState {
655 query: AhoCorasick,
656 wordwise: bool,
657 done: bool,
658}
659
660#[derive(Debug)]
661struct AutocloseRegion {
662 selection_id: usize,
663 range: Range<Anchor>,
664 pair: BracketPair,
665}
666
667#[derive(Debug)]
668struct SnippetState {
669 ranges: Vec<Vec<Range<Anchor>>>,
670 active_index: usize,
671}
672
673pub struct RenameState {
674 pub range: Range<Anchor>,
675 pub old_name: Arc<str>,
676 pub editor: ViewHandle<Editor>,
677 block_id: BlockId,
678}
679
680struct InvalidationStack<T>(Vec<T>);
681
682enum ContextMenu {
683 Completions(CompletionsMenu),
684 CodeActions(CodeActionsMenu),
685}
686
687impl ContextMenu {
688 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
689 if self.visible() {
690 match self {
691 ContextMenu::Completions(menu) => menu.select_first(cx),
692 ContextMenu::CodeActions(menu) => menu.select_first(cx),
693 }
694 true
695 } else {
696 false
697 }
698 }
699
700 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
701 if self.visible() {
702 match self {
703 ContextMenu::Completions(menu) => menu.select_prev(cx),
704 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
705 }
706 true
707 } else {
708 false
709 }
710 }
711
712 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
713 if self.visible() {
714 match self {
715 ContextMenu::Completions(menu) => menu.select_next(cx),
716 ContextMenu::CodeActions(menu) => menu.select_next(cx),
717 }
718 true
719 } else {
720 false
721 }
722 }
723
724 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
725 if self.visible() {
726 match self {
727 ContextMenu::Completions(menu) => menu.select_last(cx),
728 ContextMenu::CodeActions(menu) => menu.select_last(cx),
729 }
730 true
731 } else {
732 false
733 }
734 }
735
736 fn visible(&self) -> bool {
737 match self {
738 ContextMenu::Completions(menu) => menu.visible(),
739 ContextMenu::CodeActions(menu) => menu.visible(),
740 }
741 }
742
743 fn render(
744 &self,
745 cursor_position: DisplayPoint,
746 style: EditorStyle,
747 cx: &mut ViewContext<Editor>,
748 ) -> (DisplayPoint, AnyElement<Editor>) {
749 match self {
750 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
751 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
752 }
753 }
754}
755
756struct CompletionsMenu {
757 id: CompletionId,
758 initial_position: Anchor,
759 buffer: ModelHandle<Buffer>,
760 completions: Arc<[Completion]>,
761 match_candidates: Vec<StringMatchCandidate>,
762 matches: Arc<[StringMatch]>,
763 selected_item: usize,
764 list: UniformListState,
765}
766
767impl CompletionsMenu {
768 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
769 self.selected_item = 0;
770 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
771 cx.notify();
772 }
773
774 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
775 if self.selected_item > 0 {
776 self.selected_item -= 1;
777 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
778 }
779 cx.notify();
780 }
781
782 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
783 if self.selected_item + 1 < self.matches.len() {
784 self.selected_item += 1;
785 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
786 }
787 cx.notify();
788 }
789
790 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
791 self.selected_item = self.matches.len() - 1;
792 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
793 cx.notify();
794 }
795
796 fn visible(&self) -> bool {
797 !self.matches.is_empty()
798 }
799
800 fn render(&self, style: EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
801 enum CompletionTag {}
802
803 let completions = self.completions.clone();
804 let matches = self.matches.clone();
805 let selected_item = self.selected_item;
806 let container_style = style.autocomplete.container;
807 UniformList::new(
808 self.list.clone(),
809 matches.len(),
810 cx,
811 move |_, range, items, cx| {
812 let start_ix = range.start;
813 for (ix, mat) in matches[range].iter().enumerate() {
814 let completion = &completions[mat.candidate_id];
815 let item_ix = start_ix + ix;
816 items.push(
817 MouseEventHandler::<CompletionTag, _>::new(
818 mat.candidate_id,
819 cx,
820 |state, _| {
821 let item_style = if item_ix == selected_item {
822 style.autocomplete.selected_item
823 } else if state.hovered() {
824 style.autocomplete.hovered_item
825 } else {
826 style.autocomplete.item
827 };
828
829 Text::new(completion.label.text.clone(), style.text.clone())
830 .with_soft_wrap(false)
831 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
832 &completion.label.text,
833 style.text.color.into(),
834 styled_runs_for_code_label(
835 &completion.label,
836 &style.syntax,
837 ),
838 &mat.positions,
839 ))
840 .contained()
841 .with_style(item_style)
842 },
843 )
844 .with_cursor_style(CursorStyle::PointingHand)
845 .on_down(MouseButton::Left, move |_, this, cx| {
846 this.confirm_completion(
847 &ConfirmCompletion {
848 item_ix: Some(item_ix),
849 },
850 cx,
851 );
852 })
853 .into_any(),
854 );
855 }
856 },
857 )
858 .with_width_from_item(
859 self.matches
860 .iter()
861 .enumerate()
862 .max_by_key(|(_, mat)| {
863 self.completions[mat.candidate_id]
864 .label
865 .text
866 .chars()
867 .count()
868 })
869 .map(|(ix, _)| ix),
870 )
871 .contained()
872 .with_style(container_style)
873 .into_any()
874 }
875
876 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
877 let mut matches = if let Some(query) = query {
878 fuzzy::match_strings(
879 &self.match_candidates,
880 query,
881 query.chars().any(|c| c.is_uppercase()),
882 100,
883 &Default::default(),
884 executor,
885 )
886 .await
887 } else {
888 self.match_candidates
889 .iter()
890 .enumerate()
891 .map(|(candidate_id, candidate)| StringMatch {
892 candidate_id,
893 score: Default::default(),
894 positions: Default::default(),
895 string: candidate.string.clone(),
896 })
897 .collect()
898 };
899
900 //Remove all candidates where the query's start does not match the start of any word in the candidate
901 if let Some(query) = query {
902 if let Some(query_start) = query.chars().next() {
903 matches.retain(|string_match| {
904 split_words(&string_match.string).any(|word| {
905 //Check that the first codepoint of the word as lowercase matches the first
906 //codepoint of the query as lowercase
907 word.chars()
908 .flat_map(|codepoint| codepoint.to_lowercase())
909 .zip(query_start.to_lowercase())
910 .all(|(word_cp, query_cp)| word_cp == query_cp)
911 })
912 });
913 }
914 }
915
916 matches.sort_unstable_by_key(|mat| {
917 let completion = &self.completions[mat.candidate_id];
918 (
919 completion.lsp_completion.sort_text.as_ref(),
920 Reverse(OrderedFloat(mat.score)),
921 completion.sort_key(),
922 )
923 });
924
925 for mat in &mut matches {
926 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
927 for position in &mut mat.positions {
928 *position += filter_start;
929 }
930 }
931
932 self.matches = matches.into();
933 }
934}
935
936#[derive(Clone)]
937struct CodeActionsMenu {
938 actions: Arc<[CodeAction]>,
939 buffer: ModelHandle<Buffer>,
940 selected_item: usize,
941 list: UniformListState,
942 deployed_from_indicator: bool,
943}
944
945impl CodeActionsMenu {
946 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
947 self.selected_item = 0;
948 cx.notify()
949 }
950
951 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
952 if self.selected_item > 0 {
953 self.selected_item -= 1;
954 cx.notify()
955 }
956 }
957
958 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
959 if self.selected_item + 1 < self.actions.len() {
960 self.selected_item += 1;
961 cx.notify()
962 }
963 }
964
965 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
966 self.selected_item = self.actions.len() - 1;
967 cx.notify()
968 }
969
970 fn visible(&self) -> bool {
971 !self.actions.is_empty()
972 }
973
974 fn render(
975 &self,
976 mut cursor_position: DisplayPoint,
977 style: EditorStyle,
978 cx: &mut ViewContext<Editor>,
979 ) -> (DisplayPoint, AnyElement<Editor>) {
980 enum ActionTag {}
981
982 let container_style = style.autocomplete.container;
983 let actions = self.actions.clone();
984 let selected_item = self.selected_item;
985 let element = UniformList::new(
986 self.list.clone(),
987 actions.len(),
988 cx,
989 move |_, range, items, cx| {
990 let start_ix = range.start;
991 for (ix, action) in actions[range].iter().enumerate() {
992 let item_ix = start_ix + ix;
993 items.push(
994 MouseEventHandler::<ActionTag, _>::new(item_ix, cx, |state, _| {
995 let item_style = if item_ix == selected_item {
996 style.autocomplete.selected_item
997 } else if state.hovered() {
998 style.autocomplete.hovered_item
999 } else {
1000 style.autocomplete.item
1001 };
1002
1003 Text::new(action.lsp_action.title.clone(), style.text.clone())
1004 .with_soft_wrap(false)
1005 .contained()
1006 .with_style(item_style)
1007 })
1008 .with_cursor_style(CursorStyle::PointingHand)
1009 .on_down(MouseButton::Left, move |_, this, cx| {
1010 let workspace = this
1011 .workspace
1012 .as_ref()
1013 .and_then(|(workspace, _)| workspace.upgrade(cx));
1014 cx.window_context().defer(move |cx| {
1015 if let Some(workspace) = workspace {
1016 workspace.update(cx, |workspace, cx| {
1017 if let Some(task) = Editor::confirm_code_action(
1018 workspace,
1019 &Default::default(),
1020 cx,
1021 ) {
1022 task.detach_and_log_err(cx);
1023 }
1024 });
1025 }
1026 });
1027 })
1028 .into_any(),
1029 );
1030 }
1031 },
1032 )
1033 .with_width_from_item(
1034 self.actions
1035 .iter()
1036 .enumerate()
1037 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1038 .map(|(ix, _)| ix),
1039 )
1040 .contained()
1041 .with_style(container_style)
1042 .into_any();
1043
1044 if self.deployed_from_indicator {
1045 *cursor_position.column_mut() = 0;
1046 }
1047
1048 (cursor_position, element)
1049 }
1050}
1051
1052pub struct CopilotState {
1053 excerpt_id: Option<ExcerptId>,
1054 pending_refresh: Task<Option<()>>,
1055 pending_cycling_refresh: Task<Option<()>>,
1056 cycled: bool,
1057 completions: Vec<copilot::Completion>,
1058 active_completion_index: usize,
1059}
1060
1061impl Default for CopilotState {
1062 fn default() -> Self {
1063 Self {
1064 excerpt_id: None,
1065 pending_cycling_refresh: Task::ready(Some(())),
1066 pending_refresh: Task::ready(Some(())),
1067 completions: Default::default(),
1068 active_completion_index: 0,
1069 cycled: false,
1070 }
1071 }
1072}
1073
1074impl CopilotState {
1075 fn active_completion(&self) -> Option<&copilot::Completion> {
1076 self.completions.get(self.active_completion_index)
1077 }
1078
1079 fn text_for_active_completion(
1080 &self,
1081 cursor: Anchor,
1082 buffer: &MultiBufferSnapshot,
1083 ) -> Option<&str> {
1084 use language::ToOffset as _;
1085
1086 let completion = self.active_completion()?;
1087 let excerpt_id = self.excerpt_id?;
1088 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1089 if excerpt_id != cursor.excerpt_id
1090 || !completion.range.start.is_valid(completion_buffer)
1091 || !completion.range.end.is_valid(completion_buffer)
1092 {
1093 return None;
1094 }
1095
1096 let mut completion_range = completion.range.to_offset(&completion_buffer);
1097 let prefix_len = Self::common_prefix(
1098 completion_buffer.chars_for_range(completion_range.clone()),
1099 completion.text.chars(),
1100 );
1101 completion_range.start += prefix_len;
1102 let suffix_len = Self::common_prefix(
1103 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1104 completion.text[prefix_len..].chars().rev(),
1105 );
1106 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1107
1108 if completion_range.is_empty()
1109 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1110 {
1111 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1112 } else {
1113 None
1114 }
1115 }
1116
1117 fn cycle_completions(&mut self, direction: Direction) {
1118 match direction {
1119 Direction::Prev => {
1120 self.active_completion_index = if self.active_completion_index == 0 {
1121 self.completions.len().saturating_sub(1)
1122 } else {
1123 self.active_completion_index - 1
1124 };
1125 }
1126 Direction::Next => {
1127 if self.completions.len() == 0 {
1128 self.active_completion_index = 0
1129 } else {
1130 self.active_completion_index =
1131 (self.active_completion_index + 1) % self.completions.len();
1132 }
1133 }
1134 }
1135 }
1136
1137 fn push_completion(&mut self, new_completion: copilot::Completion) {
1138 for completion in &self.completions {
1139 if completion.text == new_completion.text && completion.range == new_completion.range {
1140 return;
1141 }
1142 }
1143 self.completions.push(new_completion);
1144 }
1145
1146 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1147 a.zip(b)
1148 .take_while(|(a, b)| a == b)
1149 .map(|(a, _)| a.len_utf8())
1150 .sum()
1151 }
1152}
1153
1154#[derive(Debug)]
1155struct ActiveDiagnosticGroup {
1156 primary_range: Range<Anchor>,
1157 primary_message: String,
1158 blocks: HashMap<BlockId, Diagnostic>,
1159 is_valid: bool,
1160}
1161
1162#[derive(Serialize, Deserialize)]
1163pub struct ClipboardSelection {
1164 pub len: usize,
1165 pub is_entire_line: bool,
1166 pub first_line_indent: u32,
1167}
1168
1169#[derive(Debug)]
1170pub struct NavigationData {
1171 cursor_anchor: Anchor,
1172 cursor_position: Point,
1173 scroll_anchor: ScrollAnchor,
1174 scroll_top_row: u32,
1175}
1176
1177pub struct EditorCreated(pub ViewHandle<Editor>);
1178
1179enum GotoDefinitionKind {
1180 Symbol,
1181 Type,
1182}
1183
1184impl Editor {
1185 pub fn single_line(
1186 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1187 cx: &mut ViewContext<Self>,
1188 ) -> Self {
1189 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1190 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1191 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
1192 }
1193
1194 pub fn multi_line(
1195 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1196 cx: &mut ViewContext<Self>,
1197 ) -> Self {
1198 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1199 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1200 Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1201 }
1202
1203 pub fn auto_height(
1204 max_lines: usize,
1205 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1206 cx: &mut ViewContext<Self>,
1207 ) -> Self {
1208 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1209 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1210 Self::new(
1211 EditorMode::AutoHeight { max_lines },
1212 buffer,
1213 None,
1214 field_editor_style,
1215 cx,
1216 )
1217 }
1218
1219 pub fn for_buffer(
1220 buffer: ModelHandle<Buffer>,
1221 project: Option<ModelHandle<Project>>,
1222 cx: &mut ViewContext<Self>,
1223 ) -> Self {
1224 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1225 Self::new(EditorMode::Full, buffer, project, None, cx)
1226 }
1227
1228 pub fn for_multibuffer(
1229 buffer: ModelHandle<MultiBuffer>,
1230 project: Option<ModelHandle<Project>>,
1231 cx: &mut ViewContext<Self>,
1232 ) -> Self {
1233 Self::new(EditorMode::Full, buffer, project, None, cx)
1234 }
1235
1236 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1237 let mut clone = Self::new(
1238 self.mode,
1239 self.buffer.clone(),
1240 self.project.clone(),
1241 self.get_field_editor_theme.clone(),
1242 cx,
1243 );
1244 self.display_map.update(cx, |display_map, cx| {
1245 let snapshot = display_map.snapshot(cx);
1246 clone.display_map.update(cx, |display_map, cx| {
1247 display_map.set_state(&snapshot, cx);
1248 });
1249 });
1250 clone.selections.clone_state(&self.selections);
1251 clone.scroll_manager.clone_state(&self.scroll_manager);
1252 clone.searchable = self.searchable;
1253 clone
1254 }
1255
1256 fn new(
1257 mode: EditorMode,
1258 buffer: ModelHandle<MultiBuffer>,
1259 project: Option<ModelHandle<Project>>,
1260 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1261 cx: &mut ViewContext<Self>,
1262 ) -> Self {
1263 let editor_view_id = cx.view_id();
1264 let display_map = cx.add_model(|cx| {
1265 let settings = settings::get::<ThemeSettings>(cx);
1266 let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
1267 DisplayMap::new(
1268 buffer.clone(),
1269 style.text.font_id,
1270 style.text.font_size,
1271 None,
1272 2,
1273 1,
1274 cx,
1275 )
1276 });
1277
1278 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1279
1280 let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1281
1282 let soft_wrap_mode_override =
1283 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1284
1285 let mut project_subscription = None;
1286 if mode == EditorMode::Full && buffer.read(cx).is_singleton() {
1287 if let Some(project) = project.as_ref() {
1288 project_subscription = Some(cx.observe(project, |_, _, cx| {
1289 cx.emit(Event::TitleChanged);
1290 }))
1291 }
1292 }
1293
1294 let mut this = Self {
1295 handle: cx.weak_handle(),
1296 buffer: buffer.clone(),
1297 display_map: display_map.clone(),
1298 selections,
1299 scroll_manager: ScrollManager::new(),
1300 columnar_selection_tail: None,
1301 add_selections_state: None,
1302 select_next_state: None,
1303 select_prev_state: None,
1304 selection_history: Default::default(),
1305 autoclose_regions: Default::default(),
1306 snippet_stack: Default::default(),
1307 select_larger_syntax_node_stack: Vec::new(),
1308 ime_transaction: Default::default(),
1309 active_diagnostics: None,
1310 soft_wrap_mode_override,
1311 get_field_editor_theme,
1312 project,
1313 focused: false,
1314 blink_manager: blink_manager.clone(),
1315 show_local_selections: true,
1316 mode,
1317 show_gutter: mode == EditorMode::Full,
1318 placeholder_text: None,
1319 highlighted_rows: None,
1320 background_highlights: Default::default(),
1321 nav_history: None,
1322 context_menu: None,
1323 mouse_context_menu: cx
1324 .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
1325 completion_tasks: Default::default(),
1326 next_completion_id: 0,
1327 available_code_actions: Default::default(),
1328 code_actions_task: Default::default(),
1329 document_highlights_task: Default::default(),
1330 pending_rename: Default::default(),
1331 searchable: true,
1332 override_text_style: None,
1333 cursor_shape: Default::default(),
1334 workspace: None,
1335 keymap_context_layers: Default::default(),
1336 input_enabled: true,
1337 read_only: false,
1338 leader_replica_id: None,
1339 remote_id: None,
1340 hover_state: Default::default(),
1341 link_go_to_definition_state: Default::default(),
1342 copilot_state: Default::default(),
1343 gutter_hovered: false,
1344 _subscriptions: vec![
1345 cx.observe(&buffer, Self::on_buffer_changed),
1346 cx.subscribe(&buffer, Self::on_buffer_event),
1347 cx.observe(&display_map, Self::on_display_map_changed),
1348 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1349 cx.observe_global::<SettingsStore, _>(Self::settings_changed),
1350 ],
1351 };
1352
1353 if let Some(project_subscription) = project_subscription {
1354 this._subscriptions.push(project_subscription);
1355 }
1356
1357 this.end_selection(cx);
1358 this.scroll_manager.show_scrollbar(cx);
1359
1360 let editor_created_event = EditorCreated(cx.handle());
1361 cx.emit_global(editor_created_event);
1362
1363 if mode == EditorMode::Full {
1364 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1365 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1366 }
1367
1368 this.report_editor_event("open", None, cx);
1369 this
1370 }
1371
1372 pub fn new_file(
1373 workspace: &mut Workspace,
1374 _: &workspace::NewFile,
1375 cx: &mut ViewContext<Workspace>,
1376 ) {
1377 let project = workspace.project().clone();
1378 if project.read(cx).is_remote() {
1379 cx.propagate_action();
1380 } else if let Some(buffer) = project
1381 .update(cx, |project, cx| project.create_buffer("", None, cx))
1382 .log_err()
1383 {
1384 workspace.add_item(
1385 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1386 cx,
1387 );
1388 }
1389 }
1390
1391 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1392 self.buffer.read(cx).replica_id()
1393 }
1394
1395 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1396 self.leader_replica_id
1397 }
1398
1399 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1400 &self.buffer
1401 }
1402
1403 fn workspace(&self, cx: &AppContext) -> Option<ViewHandle<Workspace>> {
1404 self.workspace.as_ref()?.0.upgrade(cx)
1405 }
1406
1407 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1408 self.buffer().read(cx).title(cx)
1409 }
1410
1411 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1412 EditorSnapshot {
1413 mode: self.mode,
1414 show_gutter: self.show_gutter,
1415 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1416 scroll_anchor: self.scroll_manager.anchor(),
1417 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1418 placeholder_text: self.placeholder_text.clone(),
1419 is_focused: self
1420 .handle
1421 .upgrade(cx)
1422 .map_or(false, |handle| handle.is_focused(cx)),
1423 }
1424 }
1425
1426 pub fn language_at<'a, T: ToOffset>(
1427 &self,
1428 point: T,
1429 cx: &'a AppContext,
1430 ) -> Option<Arc<Language>> {
1431 self.buffer.read(cx).language_at(point, cx)
1432 }
1433
1434 pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> {
1435 self.buffer.read(cx).read(cx).file_at(point).cloned()
1436 }
1437
1438 pub fn active_excerpt(
1439 &self,
1440 cx: &AppContext,
1441 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1442 self.buffer
1443 .read(cx)
1444 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1445 }
1446
1447 fn style(&self, cx: &AppContext) -> EditorStyle {
1448 build_style(
1449 settings::get::<ThemeSettings>(cx),
1450 self.get_field_editor_theme.as_deref(),
1451 self.override_text_style.as_deref(),
1452 cx,
1453 )
1454 }
1455
1456 pub fn mode(&self) -> EditorMode {
1457 self.mode
1458 }
1459
1460 pub fn set_placeholder_text(
1461 &mut self,
1462 placeholder_text: impl Into<Arc<str>>,
1463 cx: &mut ViewContext<Self>,
1464 ) {
1465 self.placeholder_text = Some(placeholder_text.into());
1466 cx.notify();
1467 }
1468
1469 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1470 self.cursor_shape = cursor_shape;
1471 cx.notify();
1472 }
1473
1474 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1475 if self.display_map.read(cx).clip_at_line_ends != clip {
1476 self.display_map
1477 .update(cx, |map, _| map.clip_at_line_ends = clip);
1478 }
1479 }
1480
1481 pub fn set_keymap_context_layer<Tag: 'static>(
1482 &mut self,
1483 context: KeymapContext,
1484 cx: &mut ViewContext<Self>,
1485 ) {
1486 self.keymap_context_layers
1487 .insert(TypeId::of::<Tag>(), context);
1488 cx.notify();
1489 }
1490
1491 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1492 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1493 cx.notify();
1494 }
1495
1496 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1497 self.input_enabled = input_enabled;
1498 }
1499
1500 pub fn set_read_only(&mut self, read_only: bool) {
1501 self.read_only = read_only;
1502 }
1503
1504 fn selections_did_change(
1505 &mut self,
1506 local: bool,
1507 old_cursor_position: &Anchor,
1508 cx: &mut ViewContext<Self>,
1509 ) {
1510 if self.focused && self.leader_replica_id.is_none() {
1511 self.buffer.update(cx, |buffer, cx| {
1512 buffer.set_active_selections(
1513 &self.selections.disjoint_anchors(),
1514 self.selections.line_mode,
1515 self.cursor_shape,
1516 cx,
1517 )
1518 });
1519 }
1520
1521 let display_map = self
1522 .display_map
1523 .update(cx, |display_map, cx| display_map.snapshot(cx));
1524 let buffer = &display_map.buffer_snapshot;
1525 self.add_selections_state = None;
1526 self.select_next_state = None;
1527 self.select_prev_state = None;
1528 self.select_larger_syntax_node_stack.clear();
1529 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1530 self.snippet_stack
1531 .invalidate(&self.selections.disjoint_anchors(), buffer);
1532 self.take_rename(false, cx);
1533
1534 let new_cursor_position = self.selections.newest_anchor().head();
1535
1536 self.push_to_nav_history(
1537 old_cursor_position.clone(),
1538 Some(new_cursor_position.to_point(buffer)),
1539 cx,
1540 );
1541
1542 if local {
1543 let new_cursor_position = self.selections.newest_anchor().head();
1544 let completion_menu = match self.context_menu.as_mut() {
1545 Some(ContextMenu::Completions(menu)) => Some(menu),
1546 _ => {
1547 self.context_menu.take();
1548 None
1549 }
1550 };
1551
1552 if let Some(completion_menu) = completion_menu {
1553 let cursor_position = new_cursor_position.to_offset(buffer);
1554 let (word_range, kind) =
1555 buffer.surrounding_word(completion_menu.initial_position.clone());
1556 if kind == Some(CharKind::Word)
1557 && word_range.to_inclusive().contains(&cursor_position)
1558 {
1559 let query = Self::completion_query(buffer, cursor_position);
1560 cx.background()
1561 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1562 self.show_completions(&ShowCompletions, cx);
1563 } else {
1564 self.hide_context_menu(cx);
1565 }
1566 }
1567
1568 hide_hover(self, cx);
1569
1570 if old_cursor_position.to_display_point(&display_map).row()
1571 != new_cursor_position.to_display_point(&display_map).row()
1572 {
1573 self.available_code_actions.take();
1574 }
1575 self.refresh_code_actions(cx);
1576 self.refresh_document_highlights(cx);
1577 refresh_matching_bracket_highlights(self, cx);
1578 self.discard_copilot_suggestion(cx);
1579 }
1580
1581 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1582 cx.emit(Event::SelectionsChanged { local });
1583 cx.notify();
1584 }
1585
1586 pub fn change_selections<R>(
1587 &mut self,
1588 autoscroll: Option<Autoscroll>,
1589 cx: &mut ViewContext<Self>,
1590 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1591 ) -> R {
1592 let old_cursor_position = self.selections.newest_anchor().head();
1593 self.push_to_selection_history();
1594
1595 let (changed, result) = self.selections.change_with(cx, change);
1596
1597 if changed {
1598 if let Some(autoscroll) = autoscroll {
1599 self.request_autoscroll(autoscroll, cx);
1600 }
1601 self.selections_did_change(true, &old_cursor_position, cx);
1602 }
1603
1604 result
1605 }
1606
1607 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1608 where
1609 I: IntoIterator<Item = (Range<S>, T)>,
1610 S: ToOffset,
1611 T: Into<Arc<str>>,
1612 {
1613 if self.read_only {
1614 return;
1615 }
1616
1617 self.buffer
1618 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1619 }
1620
1621 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1622 where
1623 I: IntoIterator<Item = (Range<S>, T)>,
1624 S: ToOffset,
1625 T: Into<Arc<str>>,
1626 {
1627 if self.read_only {
1628 return;
1629 }
1630
1631 self.buffer.update(cx, |buffer, cx| {
1632 buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
1633 });
1634 }
1635
1636 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
1637 self.hide_context_menu(cx);
1638
1639 match phase {
1640 SelectPhase::Begin {
1641 position,
1642 add,
1643 click_count,
1644 } => self.begin_selection(position, add, click_count, cx),
1645 SelectPhase::BeginColumnar {
1646 position,
1647 goal_column,
1648 } => self.begin_columnar_selection(position, goal_column, cx),
1649 SelectPhase::Extend {
1650 position,
1651 click_count,
1652 } => self.extend_selection(position, click_count, cx),
1653 SelectPhase::Update {
1654 position,
1655 goal_column,
1656 scroll_position,
1657 } => self.update_selection(position, goal_column, scroll_position, cx),
1658 SelectPhase::End => self.end_selection(cx),
1659 }
1660 }
1661
1662 fn extend_selection(
1663 &mut self,
1664 position: DisplayPoint,
1665 click_count: usize,
1666 cx: &mut ViewContext<Self>,
1667 ) {
1668 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1669 let tail = self.selections.newest::<usize>(cx).tail();
1670 self.begin_selection(position, false, click_count, cx);
1671
1672 let position = position.to_offset(&display_map, Bias::Left);
1673 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1674
1675 let mut pending_selection = self
1676 .selections
1677 .pending_anchor()
1678 .expect("extend_selection not called with pending selection");
1679 if position >= tail {
1680 pending_selection.start = tail_anchor;
1681 } else {
1682 pending_selection.end = tail_anchor;
1683 pending_selection.reversed = true;
1684 }
1685
1686 let mut pending_mode = self.selections.pending_mode().unwrap();
1687 match &mut pending_mode {
1688 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
1689 _ => {}
1690 }
1691
1692 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
1693 s.set_pending(pending_selection, pending_mode)
1694 });
1695 }
1696
1697 fn begin_selection(
1698 &mut self,
1699 position: DisplayPoint,
1700 add: bool,
1701 click_count: usize,
1702 cx: &mut ViewContext<Self>,
1703 ) {
1704 if !self.focused {
1705 cx.focus_self();
1706 }
1707
1708 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1709 let buffer = &display_map.buffer_snapshot;
1710 let newest_selection = self.selections.newest_anchor().clone();
1711 let position = display_map.clip_point(position, Bias::Left);
1712
1713 let start;
1714 let end;
1715 let mode;
1716 let auto_scroll;
1717 match click_count {
1718 1 => {
1719 start = buffer.anchor_before(position.to_point(&display_map));
1720 end = start.clone();
1721 mode = SelectMode::Character;
1722 auto_scroll = true;
1723 }
1724 2 => {
1725 let range = movement::surrounding_word(&display_map, position);
1726 start = buffer.anchor_before(range.start.to_point(&display_map));
1727 end = buffer.anchor_before(range.end.to_point(&display_map));
1728 mode = SelectMode::Word(start.clone()..end.clone());
1729 auto_scroll = true;
1730 }
1731 3 => {
1732 let position = display_map
1733 .clip_point(position, Bias::Left)
1734 .to_point(&display_map);
1735 let line_start = display_map.prev_line_boundary(position).0;
1736 let next_line_start = buffer.clip_point(
1737 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1738 Bias::Left,
1739 );
1740 start = buffer.anchor_before(line_start);
1741 end = buffer.anchor_before(next_line_start);
1742 mode = SelectMode::Line(start.clone()..end.clone());
1743 auto_scroll = true;
1744 }
1745 _ => {
1746 start = buffer.anchor_before(0);
1747 end = buffer.anchor_before(buffer.len());
1748 mode = SelectMode::All;
1749 auto_scroll = false;
1750 }
1751 }
1752
1753 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
1754 if !add {
1755 s.clear_disjoint();
1756 } else if click_count > 1 {
1757 s.delete(newest_selection.id)
1758 }
1759
1760 s.set_pending_anchor_range(start..end, mode);
1761 });
1762 }
1763
1764 fn begin_columnar_selection(
1765 &mut self,
1766 position: DisplayPoint,
1767 goal_column: u32,
1768 cx: &mut ViewContext<Self>,
1769 ) {
1770 if !self.focused {
1771 cx.focus_self();
1772 }
1773
1774 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1775 let tail = self.selections.newest::<Point>(cx).tail();
1776 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1777
1778 self.select_columns(
1779 tail.to_display_point(&display_map),
1780 position,
1781 goal_column,
1782 &display_map,
1783 cx,
1784 );
1785 }
1786
1787 fn update_selection(
1788 &mut self,
1789 position: DisplayPoint,
1790 goal_column: u32,
1791 scroll_position: Vector2F,
1792 cx: &mut ViewContext<Self>,
1793 ) {
1794 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1795
1796 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1797 let tail = tail.to_display_point(&display_map);
1798 self.select_columns(tail, position, goal_column, &display_map, cx);
1799 } else if let Some(mut pending) = self.selections.pending_anchor() {
1800 let buffer = self.buffer.read(cx).snapshot(cx);
1801 let head;
1802 let tail;
1803 let mode = self.selections.pending_mode().unwrap();
1804 match &mode {
1805 SelectMode::Character => {
1806 head = position.to_point(&display_map);
1807 tail = pending.tail().to_point(&buffer);
1808 }
1809 SelectMode::Word(original_range) => {
1810 let original_display_range = original_range.start.to_display_point(&display_map)
1811 ..original_range.end.to_display_point(&display_map);
1812 let original_buffer_range = original_display_range.start.to_point(&display_map)
1813 ..original_display_range.end.to_point(&display_map);
1814 if movement::is_inside_word(&display_map, position)
1815 || original_display_range.contains(&position)
1816 {
1817 let word_range = movement::surrounding_word(&display_map, position);
1818 if word_range.start < original_display_range.start {
1819 head = word_range.start.to_point(&display_map);
1820 } else {
1821 head = word_range.end.to_point(&display_map);
1822 }
1823 } else {
1824 head = position.to_point(&display_map);
1825 }
1826
1827 if head <= original_buffer_range.start {
1828 tail = original_buffer_range.end;
1829 } else {
1830 tail = original_buffer_range.start;
1831 }
1832 }
1833 SelectMode::Line(original_range) => {
1834 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1835
1836 let position = display_map
1837 .clip_point(position, Bias::Left)
1838 .to_point(&display_map);
1839 let line_start = display_map.prev_line_boundary(position).0;
1840 let next_line_start = buffer.clip_point(
1841 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1842 Bias::Left,
1843 );
1844
1845 if line_start < original_range.start {
1846 head = line_start
1847 } else {
1848 head = next_line_start
1849 }
1850
1851 if head <= original_range.start {
1852 tail = original_range.end;
1853 } else {
1854 tail = original_range.start;
1855 }
1856 }
1857 SelectMode::All => {
1858 return;
1859 }
1860 };
1861
1862 if head < tail {
1863 pending.start = buffer.anchor_before(head);
1864 pending.end = buffer.anchor_before(tail);
1865 pending.reversed = true;
1866 } else {
1867 pending.start = buffer.anchor_before(tail);
1868 pending.end = buffer.anchor_before(head);
1869 pending.reversed = false;
1870 }
1871
1872 self.change_selections(None, cx, |s| {
1873 s.set_pending(pending, mode);
1874 });
1875 } else {
1876 log::error!("update_selection dispatched with no pending selection");
1877 return;
1878 }
1879
1880 self.set_scroll_position(scroll_position, cx);
1881 cx.notify();
1882 }
1883
1884 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1885 self.columnar_selection_tail.take();
1886 if self.selections.pending_anchor().is_some() {
1887 let selections = self.selections.all::<usize>(cx);
1888 self.change_selections(None, cx, |s| {
1889 s.select(selections);
1890 s.clear_pending();
1891 });
1892 }
1893 }
1894
1895 fn select_columns(
1896 &mut self,
1897 tail: DisplayPoint,
1898 head: DisplayPoint,
1899 goal_column: u32,
1900 display_map: &DisplaySnapshot,
1901 cx: &mut ViewContext<Self>,
1902 ) {
1903 let start_row = cmp::min(tail.row(), head.row());
1904 let end_row = cmp::max(tail.row(), head.row());
1905 let start_column = cmp::min(tail.column(), goal_column);
1906 let end_column = cmp::max(tail.column(), goal_column);
1907 let reversed = start_column < tail.column();
1908
1909 let selection_ranges = (start_row..=end_row)
1910 .filter_map(|row| {
1911 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1912 let start = display_map
1913 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1914 .to_point(display_map);
1915 let end = display_map
1916 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1917 .to_point(display_map);
1918 if reversed {
1919 Some(end..start)
1920 } else {
1921 Some(start..end)
1922 }
1923 } else {
1924 None
1925 }
1926 })
1927 .collect::<Vec<_>>();
1928
1929 self.change_selections(None, cx, |s| {
1930 s.select_ranges(selection_ranges);
1931 });
1932 cx.notify();
1933 }
1934
1935 pub fn has_pending_nonempty_selection(&self) -> bool {
1936 let pending_nonempty_selection = match self.selections.pending_anchor() {
1937 Some(Selection { start, end, .. }) => start != end,
1938 None => false,
1939 };
1940 pending_nonempty_selection || self.columnar_selection_tail.is_some()
1941 }
1942
1943 pub fn has_pending_selection(&self) -> bool {
1944 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
1945 }
1946
1947 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1948 if self.take_rename(false, cx).is_some() {
1949 return;
1950 }
1951
1952 if hide_hover(self, cx) {
1953 return;
1954 }
1955
1956 if self.hide_context_menu(cx).is_some() {
1957 return;
1958 }
1959
1960 if self.discard_copilot_suggestion(cx) {
1961 return;
1962 }
1963
1964 if self.snippet_stack.pop().is_some() {
1965 return;
1966 }
1967
1968 if self.mode == EditorMode::Full {
1969 if self.active_diagnostics.is_some() {
1970 self.dismiss_diagnostics(cx);
1971 return;
1972 }
1973
1974 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
1975 return;
1976 }
1977 }
1978
1979 cx.propagate_action();
1980 }
1981
1982 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1983 let text: Arc<str> = text.into();
1984
1985 if self.read_only {
1986 return;
1987 }
1988 if !self.input_enabled {
1989 cx.emit(Event::InputIgnored { text });
1990 return;
1991 }
1992
1993 let selections = self.selections.all_adjusted(cx);
1994 let mut edits = Vec::new();
1995 let mut new_selections = Vec::with_capacity(selections.len());
1996 let mut new_autoclose_regions = Vec::new();
1997 let snapshot = self.buffer.read(cx).read(cx);
1998
1999 for (selection, autoclose_region) in
2000 self.selections_with_autoclose_regions(selections, &snapshot)
2001 {
2002 if let Some(language) = snapshot.language_scope_at(selection.head()) {
2003 // Determine if the inserted text matches the opening or closing
2004 // bracket of any of this language's bracket pairs.
2005 let mut bracket_pair = None;
2006 let mut is_bracket_pair_start = false;
2007 for (pair, enabled) in language.brackets() {
2008 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2009 bracket_pair = Some(pair.clone());
2010 is_bracket_pair_start = true;
2011 break;
2012 } else if pair.end.as_str() == text.as_ref() {
2013 bracket_pair = Some(pair.clone());
2014 break;
2015 }
2016 }
2017
2018 if let Some(bracket_pair) = bracket_pair {
2019 if selection.is_empty() {
2020 if is_bracket_pair_start {
2021 let prefix_len = bracket_pair.start.len() - text.len();
2022
2023 // If the inserted text is a suffix of an opening bracket and the
2024 // selection is preceded by the rest of the opening bracket, then
2025 // insert the closing bracket.
2026 let following_text_allows_autoclose = snapshot
2027 .chars_at(selection.start)
2028 .next()
2029 .map_or(true, |c| language.should_autoclose_before(c));
2030 let preceding_text_matches_prefix = prefix_len == 0
2031 || (selection.start.column >= (prefix_len as u32)
2032 && snapshot.contains_str_at(
2033 Point::new(
2034 selection.start.row,
2035 selection.start.column - (prefix_len as u32),
2036 ),
2037 &bracket_pair.start[..prefix_len],
2038 ));
2039 if following_text_allows_autoclose && preceding_text_matches_prefix {
2040 let anchor = snapshot.anchor_before(selection.end);
2041 new_selections.push((selection.map(|_| anchor), text.len()));
2042 new_autoclose_regions.push((
2043 anchor,
2044 text.len(),
2045 selection.id,
2046 bracket_pair.clone(),
2047 ));
2048 edits.push((
2049 selection.range(),
2050 format!("{}{}", text, bracket_pair.end).into(),
2051 ));
2052 continue;
2053 }
2054 }
2055
2056 if let Some(region) = autoclose_region {
2057 // If the selection is followed by an auto-inserted closing bracket,
2058 // then don't insert that closing bracket again; just move the selection
2059 // past the closing bracket.
2060 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2061 && text.as_ref() == region.pair.end.as_str();
2062 if should_skip {
2063 let anchor = snapshot.anchor_after(selection.end);
2064 new_selections
2065 .push((selection.map(|_| anchor), region.pair.end.len()));
2066 continue;
2067 }
2068 }
2069 }
2070 // If an opening bracket is 1 character long and is typed while
2071 // text is selected, then surround that text with the bracket pair.
2072 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2073 edits.push((selection.start..selection.start, text.clone()));
2074 edits.push((
2075 selection.end..selection.end,
2076 bracket_pair.end.as_str().into(),
2077 ));
2078 new_selections.push((
2079 Selection {
2080 id: selection.id,
2081 start: snapshot.anchor_after(selection.start),
2082 end: snapshot.anchor_before(selection.end),
2083 reversed: selection.reversed,
2084 goal: selection.goal,
2085 },
2086 0,
2087 ));
2088 continue;
2089 }
2090 }
2091 }
2092
2093 // If not handling any auto-close operation, then just replace the selected
2094 // text with the given input and move the selection to the end of the
2095 // newly inserted text.
2096 let anchor = snapshot.anchor_after(selection.end);
2097 new_selections.push((selection.map(|_| anchor), 0));
2098 edits.push((selection.start..selection.end, text.clone()));
2099 }
2100
2101 drop(snapshot);
2102 self.transact(cx, |this, cx| {
2103 this.buffer.update(cx, |buffer, cx| {
2104 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
2105 });
2106
2107 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2108 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2109 let snapshot = this.buffer.read(cx).read(cx);
2110 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2111 .zip(new_selection_deltas)
2112 .map(|(selection, delta)| selection.map(|e| e + delta))
2113 .collect::<Vec<_>>();
2114
2115 let mut i = 0;
2116 for (position, delta, selection_id, pair) in new_autoclose_regions {
2117 let position = position.to_offset(&snapshot) + delta;
2118 let start = snapshot.anchor_before(position);
2119 let end = snapshot.anchor_after(position);
2120 while let Some(existing_state) = this.autoclose_regions.get(i) {
2121 match existing_state.range.start.cmp(&start, &snapshot) {
2122 Ordering::Less => i += 1,
2123 Ordering::Greater => break,
2124 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2125 Ordering::Less => i += 1,
2126 Ordering::Equal => break,
2127 Ordering::Greater => break,
2128 },
2129 }
2130 }
2131 this.autoclose_regions.insert(
2132 i,
2133 AutocloseRegion {
2134 selection_id,
2135 range: start..end,
2136 pair,
2137 },
2138 );
2139 }
2140
2141 drop(snapshot);
2142 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2143 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2144
2145 // When buffer contents is updated and caret is moved, try triggering on type formatting.
2146 if settings::get::<EditorSettings>(cx).use_on_type_format {
2147 if let Some(on_type_format_task) =
2148 this.trigger_on_type_formatting(text.to_string(), cx)
2149 {
2150 on_type_format_task.detach_and_log_err(cx);
2151 }
2152 }
2153
2154 if let Some(hints_task) = this.request_inlay_hints(cx) {
2155 hints_task.detach_and_log_err(cx);
2156 }
2157
2158 if had_active_copilot_suggestion {
2159 this.refresh_copilot_suggestions(true, cx);
2160 if !this.has_active_copilot_suggestion(cx) {
2161 this.trigger_completion_on_input(&text, cx);
2162 }
2163 } else {
2164 this.trigger_completion_on_input(&text, cx);
2165 this.refresh_copilot_suggestions(true, cx);
2166 }
2167 });
2168 }
2169
2170 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2171 self.transact(cx, |this, cx| {
2172 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2173 let selections = this.selections.all::<usize>(cx);
2174 let multi_buffer = this.buffer.read(cx);
2175 let buffer = multi_buffer.snapshot(cx);
2176 selections
2177 .iter()
2178 .map(|selection| {
2179 let start_point = selection.start.to_point(&buffer);
2180 let mut indent = buffer.indent_size_for_line(start_point.row);
2181 indent.len = cmp::min(indent.len, start_point.column);
2182 let start = selection.start;
2183 let end = selection.end;
2184 let is_cursor = start == end;
2185 let language_scope = buffer.language_scope_at(start);
2186 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2187 &language_scope
2188 {
2189 let leading_whitespace_len = buffer
2190 .reversed_chars_at(start)
2191 .take_while(|c| c.is_whitespace() && *c != '\n')
2192 .map(|c| c.len_utf8())
2193 .sum::<usize>();
2194
2195 let trailing_whitespace_len = buffer
2196 .chars_at(end)
2197 .take_while(|c| c.is_whitespace() && *c != '\n')
2198 .map(|c| c.len_utf8())
2199 .sum::<usize>();
2200
2201 let insert_extra_newline =
2202 language.brackets().any(|(pair, enabled)| {
2203 let pair_start = pair.start.trim_end();
2204 let pair_end = pair.end.trim_start();
2205
2206 enabled
2207 && pair.newline
2208 && buffer.contains_str_at(
2209 end + trailing_whitespace_len,
2210 pair_end,
2211 )
2212 && buffer.contains_str_at(
2213 (start - leading_whitespace_len)
2214 .saturating_sub(pair_start.len()),
2215 pair_start,
2216 )
2217 });
2218 // Comment extension on newline is allowed only for cursor selections
2219 let comment_delimiter = language.line_comment_prefix().filter(|_| {
2220 let is_comment_extension_enabled =
2221 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2222 is_cursor && is_comment_extension_enabled
2223 });
2224 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2225 buffer
2226 .buffer_line_for_row(start_point.row)
2227 .is_some_and(|(snapshot, range)| {
2228 let mut index_of_first_non_whitespace = 0;
2229 let line_starts_with_comment = snapshot
2230 .chars_for_range(range)
2231 .skip_while(|c| {
2232 let should_skip = c.is_whitespace();
2233 if should_skip {
2234 index_of_first_non_whitespace += 1;
2235 }
2236 should_skip
2237 })
2238 .take(delimiter.len())
2239 .eq(delimiter.chars());
2240 let cursor_is_placed_after_comment_marker =
2241 index_of_first_non_whitespace + delimiter.len()
2242 <= start_point.column as usize;
2243 line_starts_with_comment
2244 && cursor_is_placed_after_comment_marker
2245 })
2246 .then(|| delimiter.clone())
2247 } else {
2248 None
2249 };
2250 (comment_delimiter, insert_extra_newline)
2251 } else {
2252 (None, false)
2253 };
2254
2255 let capacity_for_delimiter = comment_delimiter
2256 .as_deref()
2257 .map(str::len)
2258 .unwrap_or_default();
2259 let mut new_text =
2260 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2261 new_text.push_str("\n");
2262 new_text.extend(indent.chars());
2263 if let Some(delimiter) = &comment_delimiter {
2264 new_text.push_str(&delimiter);
2265 }
2266 if insert_extra_newline {
2267 new_text = new_text.repeat(2);
2268 }
2269
2270 let anchor = buffer.anchor_after(end);
2271 let new_selection = selection.map(|_| anchor);
2272 (
2273 (start..end, new_text),
2274 (insert_extra_newline, new_selection),
2275 )
2276 })
2277 .unzip()
2278 };
2279
2280 this.edit_with_autoindent(edits, cx);
2281 let buffer = this.buffer.read(cx).snapshot(cx);
2282 let new_selections = selection_fixup_info
2283 .into_iter()
2284 .map(|(extra_newline_inserted, new_selection)| {
2285 let mut cursor = new_selection.end.to_point(&buffer);
2286 if extra_newline_inserted {
2287 cursor.row -= 1;
2288 cursor.column = buffer.line_len(cursor.row);
2289 }
2290 new_selection.map(|_| cursor)
2291 })
2292 .collect();
2293
2294 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2295 this.refresh_copilot_suggestions(true, cx);
2296 });
2297 }
2298
2299 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2300 let buffer = self.buffer.read(cx);
2301 let snapshot = buffer.snapshot(cx);
2302
2303 let mut edits = Vec::new();
2304 let mut rows = Vec::new();
2305 let mut rows_inserted = 0;
2306
2307 for selection in self.selections.all_adjusted(cx) {
2308 let cursor = selection.head();
2309 let row = cursor.row;
2310
2311 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2312
2313 let newline = "\n".to_string();
2314 edits.push((start_of_line..start_of_line, newline));
2315
2316 rows.push(row + rows_inserted);
2317 rows_inserted += 1;
2318 }
2319
2320 self.transact(cx, |editor, cx| {
2321 editor.edit(edits, cx);
2322
2323 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2324 let mut index = 0;
2325 s.move_cursors_with(|map, _, _| {
2326 let row = rows[index];
2327 index += 1;
2328
2329 let point = Point::new(row, 0);
2330 let boundary = map.next_line_boundary(point).1;
2331 let clipped = map.clip_point(boundary, Bias::Left);
2332
2333 (clipped, SelectionGoal::None)
2334 });
2335 });
2336
2337 let mut indent_edits = Vec::new();
2338 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2339 for row in rows {
2340 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2341 for (row, indent) in indents {
2342 if indent.len == 0 {
2343 continue;
2344 }
2345
2346 let text = match indent.kind {
2347 IndentKind::Space => " ".repeat(indent.len as usize),
2348 IndentKind::Tab => "\t".repeat(indent.len as usize),
2349 };
2350 let point = Point::new(row, 0);
2351 indent_edits.push((point..point, text));
2352 }
2353 }
2354 editor.edit(indent_edits, cx);
2355 });
2356 }
2357
2358 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2359 let buffer = self.buffer.read(cx);
2360 let snapshot = buffer.snapshot(cx);
2361
2362 let mut edits = Vec::new();
2363 let mut rows = Vec::new();
2364 let mut rows_inserted = 0;
2365
2366 for selection in self.selections.all_adjusted(cx) {
2367 let cursor = selection.head();
2368 let row = cursor.row;
2369
2370 let point = Point::new(row + 1, 0);
2371 let start_of_line = snapshot.clip_point(point, Bias::Left);
2372
2373 let newline = "\n".to_string();
2374 edits.push((start_of_line..start_of_line, newline));
2375
2376 rows_inserted += 1;
2377 rows.push(row + rows_inserted);
2378 }
2379
2380 self.transact(cx, |editor, cx| {
2381 editor.edit(edits, cx);
2382
2383 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2384 let mut index = 0;
2385 s.move_cursors_with(|map, _, _| {
2386 let row = rows[index];
2387 index += 1;
2388
2389 let point = Point::new(row, 0);
2390 let boundary = map.next_line_boundary(point).1;
2391 let clipped = map.clip_point(boundary, Bias::Left);
2392
2393 (clipped, SelectionGoal::None)
2394 });
2395 });
2396
2397 let mut indent_edits = Vec::new();
2398 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2399 for row in rows {
2400 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2401 for (row, indent) in indents {
2402 if indent.len == 0 {
2403 continue;
2404 }
2405
2406 let text = match indent.kind {
2407 IndentKind::Space => " ".repeat(indent.len as usize),
2408 IndentKind::Tab => "\t".repeat(indent.len as usize),
2409 };
2410 let point = Point::new(row, 0);
2411 indent_edits.push((point..point, text));
2412 }
2413 }
2414 editor.edit(indent_edits, cx);
2415 });
2416 }
2417
2418 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2419 self.insert_with_autoindent_mode(
2420 text,
2421 Some(AutoindentMode::Block {
2422 original_indent_columns: Vec::new(),
2423 }),
2424 cx,
2425 );
2426 }
2427
2428 fn insert_with_autoindent_mode(
2429 &mut self,
2430 text: &str,
2431 autoindent_mode: Option<AutoindentMode>,
2432 cx: &mut ViewContext<Self>,
2433 ) {
2434 if self.read_only {
2435 return;
2436 }
2437
2438 let text: Arc<str> = text.into();
2439 self.transact(cx, |this, cx| {
2440 let old_selections = this.selections.all_adjusted(cx);
2441 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2442 let anchors = {
2443 let snapshot = buffer.read(cx);
2444 old_selections
2445 .iter()
2446 .map(|s| {
2447 let anchor = snapshot.anchor_after(s.head());
2448 s.map(|_| anchor)
2449 })
2450 .collect::<Vec<_>>()
2451 };
2452 buffer.edit(
2453 old_selections
2454 .iter()
2455 .map(|s| (s.start..s.end, text.clone())),
2456 autoindent_mode,
2457 cx,
2458 );
2459 anchors
2460 });
2461
2462 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2463 s.select_anchors(selection_anchors);
2464 })
2465 });
2466 }
2467
2468 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2469 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2470 return;
2471 }
2472
2473 let selection = self.selections.newest_anchor();
2474 if self
2475 .buffer
2476 .read(cx)
2477 .is_completion_trigger(selection.head(), text, cx)
2478 {
2479 self.show_completions(&ShowCompletions, cx);
2480 } else {
2481 self.hide_context_menu(cx);
2482 }
2483 }
2484
2485 /// If any empty selections is touching the start of its innermost containing autoclose
2486 /// region, expand it to select the brackets.
2487 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2488 let selections = self.selections.all::<usize>(cx);
2489 let buffer = self.buffer.read(cx).read(cx);
2490 let mut new_selections = Vec::new();
2491 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2492 if let (Some(region), true) = (region, selection.is_empty()) {
2493 let mut range = region.range.to_offset(&buffer);
2494 if selection.start == range.start {
2495 if range.start >= region.pair.start.len() {
2496 range.start -= region.pair.start.len();
2497 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2498 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2499 range.end += region.pair.end.len();
2500 selection.start = range.start;
2501 selection.end = range.end;
2502 }
2503 }
2504 }
2505 }
2506 }
2507 new_selections.push(selection);
2508 }
2509
2510 drop(buffer);
2511 self.change_selections(None, cx, |selections| selections.select(new_selections));
2512 }
2513
2514 /// Iterate the given selections, and for each one, find the smallest surrounding
2515 /// autoclose region. This uses the ordering of the selections and the autoclose
2516 /// regions to avoid repeated comparisons.
2517 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2518 &'a self,
2519 selections: impl IntoIterator<Item = Selection<D>>,
2520 buffer: &'a MultiBufferSnapshot,
2521 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2522 let mut i = 0;
2523 let mut regions = self.autoclose_regions.as_slice();
2524 selections.into_iter().map(move |selection| {
2525 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2526
2527 let mut enclosing = None;
2528 while let Some(pair_state) = regions.get(i) {
2529 if pair_state.range.end.to_offset(buffer) < range.start {
2530 regions = ®ions[i + 1..];
2531 i = 0;
2532 } else if pair_state.range.start.to_offset(buffer) > range.end {
2533 break;
2534 } else if pair_state.selection_id == selection.id {
2535 enclosing = Some(pair_state);
2536 i += 1;
2537 }
2538 }
2539
2540 (selection.clone(), enclosing)
2541 })
2542 }
2543
2544 /// Remove any autoclose regions that no longer contain their selection.
2545 fn invalidate_autoclose_regions(
2546 &mut self,
2547 mut selections: &[Selection<Anchor>],
2548 buffer: &MultiBufferSnapshot,
2549 ) {
2550 self.autoclose_regions.retain(|state| {
2551 let mut i = 0;
2552 while let Some(selection) = selections.get(i) {
2553 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2554 selections = &selections[1..];
2555 continue;
2556 }
2557 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2558 break;
2559 }
2560 if selection.id == state.selection_id {
2561 return true;
2562 } else {
2563 i += 1;
2564 }
2565 }
2566 false
2567 });
2568 }
2569
2570 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2571 let offset = position.to_offset(buffer);
2572 let (word_range, kind) = buffer.surrounding_word(offset);
2573 if offset > word_range.start && kind == Some(CharKind::Word) {
2574 Some(
2575 buffer
2576 .text_for_range(word_range.start..offset)
2577 .collect::<String>(),
2578 )
2579 } else {
2580 None
2581 }
2582 }
2583
2584 // TODO kb proper inlay hints handling
2585 fn request_inlay_hints(&self, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
2586 let project = self.project.as_ref()?;
2587 let position = self.selections.newest_anchor().head();
2588 let (buffer, _) = self
2589 .buffer
2590 .read(cx)
2591 .text_anchor_for_position(position.clone(), cx)?;
2592
2593 let end = buffer.read(cx).len();
2594 let inlay_hints_task = project.update(cx, |project, cx| {
2595 project.inlay_hints(buffer.clone(), 0..end, cx)
2596 });
2597
2598 Some(cx.spawn(|_, _| async move {
2599 let inlay_hints = inlay_hints_task.await?;
2600 dbg!(inlay_hints);
2601 Ok(())
2602 }))
2603 }
2604
2605 fn trigger_on_type_formatting(
2606 &self,
2607 input: String,
2608 cx: &mut ViewContext<Self>,
2609 ) -> Option<Task<Result<()>>> {
2610 if input.len() != 1 {
2611 return None;
2612 }
2613
2614 let project = self.project.as_ref()?;
2615 let position = self.selections.newest_anchor().head();
2616 let (buffer, buffer_position) = self
2617 .buffer
2618 .read(cx)
2619 .text_anchor_for_position(position.clone(), cx)?;
2620
2621 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2622 // hence we do LSP request & edit on host side only — add formats to host's history.
2623 let push_to_lsp_host_history = true;
2624 // If this is not the host, append its history with new edits.
2625 let push_to_client_history = project.read(cx).is_remote();
2626
2627 let on_type_formatting = project.update(cx, |project, cx| {
2628 project.on_type_format(
2629 buffer.clone(),
2630 buffer_position,
2631 input,
2632 push_to_lsp_host_history,
2633 cx,
2634 )
2635 });
2636 Some(cx.spawn(|editor, mut cx| async move {
2637 if let Some(transaction) = on_type_formatting.await? {
2638 if push_to_client_history {
2639 buffer.update(&mut cx, |buffer, _| {
2640 buffer.push_transaction(transaction, Instant::now());
2641 });
2642 }
2643 editor.update(&mut cx, |editor, cx| {
2644 editor.refresh_document_highlights(cx);
2645 })?;
2646 }
2647 Ok(())
2648 }))
2649 }
2650
2651 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2652 if self.pending_rename.is_some() {
2653 return;
2654 }
2655
2656 let project = if let Some(project) = self.project.clone() {
2657 project
2658 } else {
2659 return;
2660 };
2661
2662 let position = self.selections.newest_anchor().head();
2663 let (buffer, buffer_position) = if let Some(output) = self
2664 .buffer
2665 .read(cx)
2666 .text_anchor_for_position(position.clone(), cx)
2667 {
2668 output
2669 } else {
2670 return;
2671 };
2672
2673 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2674 let completions = project.update(cx, |project, cx| {
2675 project.completions(&buffer, buffer_position, cx)
2676 });
2677
2678 let id = post_inc(&mut self.next_completion_id);
2679 let task = cx.spawn(|this, mut cx| {
2680 async move {
2681 let menu = if let Some(completions) = completions.await.log_err() {
2682 let mut menu = CompletionsMenu {
2683 id,
2684 initial_position: position,
2685 match_candidates: completions
2686 .iter()
2687 .enumerate()
2688 .map(|(id, completion)| {
2689 StringMatchCandidate::new(
2690 id,
2691 completion.label.text[completion.label.filter_range.clone()]
2692 .into(),
2693 )
2694 })
2695 .collect(),
2696 buffer,
2697 completions: completions.into(),
2698 matches: Vec::new().into(),
2699 selected_item: 0,
2700 list: Default::default(),
2701 };
2702 menu.filter(query.as_deref(), cx.background()).await;
2703 if menu.matches.is_empty() {
2704 None
2705 } else {
2706 Some(menu)
2707 }
2708 } else {
2709 None
2710 };
2711
2712 this.update(&mut cx, |this, cx| {
2713 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2714
2715 match this.context_menu.as_ref() {
2716 None => {}
2717 Some(ContextMenu::Completions(prev_menu)) => {
2718 if prev_menu.id > id {
2719 return;
2720 }
2721 }
2722 _ => return,
2723 }
2724
2725 if this.focused && menu.is_some() {
2726 let menu = menu.unwrap();
2727 this.show_context_menu(ContextMenu::Completions(menu), cx);
2728 } else if this.completion_tasks.is_empty() {
2729 // If there are no more completion tasks and the last menu was
2730 // empty, we should hide it. If it was already hidden, we should
2731 // also show the copilot suggestion when available.
2732 if this.hide_context_menu(cx).is_none() {
2733 this.update_visible_copilot_suggestion(cx);
2734 }
2735 }
2736 })?;
2737
2738 Ok::<_, anyhow::Error>(())
2739 }
2740 .log_err()
2741 });
2742 self.completion_tasks.push((id, task));
2743 }
2744
2745 pub fn confirm_completion(
2746 &mut self,
2747 action: &ConfirmCompletion,
2748 cx: &mut ViewContext<Self>,
2749 ) -> Option<Task<Result<()>>> {
2750 use language::ToOffset as _;
2751
2752 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2753 menu
2754 } else {
2755 return None;
2756 };
2757
2758 let mat = completions_menu
2759 .matches
2760 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2761 let buffer_handle = completions_menu.buffer;
2762 let completion = completions_menu.completions.get(mat.candidate_id)?;
2763
2764 let snippet;
2765 let text;
2766 if completion.is_snippet() {
2767 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2768 text = snippet.as_ref().unwrap().text.clone();
2769 } else {
2770 snippet = None;
2771 text = completion.new_text.clone();
2772 };
2773 let selections = self.selections.all::<usize>(cx);
2774 let buffer = buffer_handle.read(cx);
2775 let old_range = completion.old_range.to_offset(buffer);
2776 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2777
2778 let newest_selection = self.selections.newest_anchor();
2779 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2780 return None;
2781 }
2782
2783 let lookbehind = newest_selection
2784 .start
2785 .text_anchor
2786 .to_offset(buffer)
2787 .saturating_sub(old_range.start);
2788 let lookahead = old_range
2789 .end
2790 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2791 let mut common_prefix_len = old_text
2792 .bytes()
2793 .zip(text.bytes())
2794 .take_while(|(a, b)| a == b)
2795 .count();
2796
2797 let snapshot = self.buffer.read(cx).snapshot(cx);
2798 let mut ranges = Vec::new();
2799 for selection in &selections {
2800 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2801 let start = selection.start.saturating_sub(lookbehind);
2802 let end = selection.end + lookahead;
2803 ranges.push(start + common_prefix_len..end);
2804 } else {
2805 common_prefix_len = 0;
2806 ranges.clear();
2807 ranges.extend(selections.iter().map(|s| {
2808 if s.id == newest_selection.id {
2809 old_range.clone()
2810 } else {
2811 s.start..s.end
2812 }
2813 }));
2814 break;
2815 }
2816 }
2817 let text = &text[common_prefix_len..];
2818
2819 self.transact(cx, |this, cx| {
2820 if let Some(mut snippet) = snippet {
2821 snippet.text = text.to_string();
2822 for tabstop in snippet.tabstops.iter_mut().flatten() {
2823 tabstop.start -= common_prefix_len as isize;
2824 tabstop.end -= common_prefix_len as isize;
2825 }
2826
2827 this.insert_snippet(&ranges, snippet, cx).log_err();
2828 } else {
2829 this.buffer.update(cx, |buffer, cx| {
2830 buffer.edit(
2831 ranges.iter().map(|range| (range.clone(), text)),
2832 Some(AutoindentMode::EachLine),
2833 cx,
2834 );
2835 });
2836 }
2837
2838 this.refresh_copilot_suggestions(true, cx);
2839 });
2840
2841 let project = self.project.clone()?;
2842 let apply_edits = project.update(cx, |project, cx| {
2843 project.apply_additional_edits_for_completion(
2844 buffer_handle,
2845 completion.clone(),
2846 true,
2847 cx,
2848 )
2849 });
2850 Some(cx.foreground().spawn(async move {
2851 apply_edits.await?;
2852 Ok(())
2853 }))
2854 }
2855
2856 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2857 if matches!(
2858 self.context_menu.as_ref(),
2859 Some(ContextMenu::CodeActions(_))
2860 ) {
2861 self.context_menu.take();
2862 cx.notify();
2863 return;
2864 }
2865
2866 let deployed_from_indicator = action.deployed_from_indicator;
2867 let mut task = self.code_actions_task.take();
2868 cx.spawn(|this, mut cx| async move {
2869 while let Some(prev_task) = task {
2870 prev_task.await;
2871 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
2872 }
2873
2874 this.update(&mut cx, |this, cx| {
2875 if this.focused {
2876 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2877 this.show_context_menu(
2878 ContextMenu::CodeActions(CodeActionsMenu {
2879 buffer,
2880 actions,
2881 selected_item: Default::default(),
2882 list: Default::default(),
2883 deployed_from_indicator,
2884 }),
2885 cx,
2886 );
2887 }
2888 }
2889 })?;
2890
2891 Ok::<_, anyhow::Error>(())
2892 })
2893 .detach_and_log_err(cx);
2894 }
2895
2896 pub fn confirm_code_action(
2897 workspace: &mut Workspace,
2898 action: &ConfirmCodeAction,
2899 cx: &mut ViewContext<Workspace>,
2900 ) -> Option<Task<Result<()>>> {
2901 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2902 let actions_menu = if let ContextMenu::CodeActions(menu) =
2903 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2904 {
2905 menu
2906 } else {
2907 return None;
2908 };
2909 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2910 let action = actions_menu.actions.get(action_ix)?.clone();
2911 let title = action.lsp_action.title.clone();
2912 let buffer = actions_menu.buffer;
2913
2914 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2915 project.apply_code_action(buffer, action, true, cx)
2916 });
2917 let editor = editor.downgrade();
2918 Some(cx.spawn(|workspace, cx| async move {
2919 let project_transaction = apply_code_actions.await?;
2920 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
2921 }))
2922 }
2923
2924 async fn open_project_transaction(
2925 this: &WeakViewHandle<Editor>,
2926 workspace: WeakViewHandle<Workspace>,
2927 transaction: ProjectTransaction,
2928 title: String,
2929 mut cx: AsyncAppContext,
2930 ) -> Result<()> {
2931 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
2932
2933 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2934 entries.sort_unstable_by_key(|(buffer, _)| {
2935 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2936 });
2937
2938 // If the project transaction's edits are all contained within this editor, then
2939 // avoid opening a new editor to display them.
2940
2941 if let Some((buffer, transaction)) = entries.first() {
2942 if entries.len() == 1 {
2943 let excerpt = this.read_with(&cx, |editor, cx| {
2944 editor
2945 .buffer()
2946 .read(cx)
2947 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2948 })?;
2949 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2950 if excerpted_buffer == *buffer {
2951 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
2952 let excerpt_range = excerpt_range.to_offset(buffer);
2953 buffer
2954 .edited_ranges_for_transaction::<usize>(transaction)
2955 .all(|range| {
2956 excerpt_range.start <= range.start
2957 && excerpt_range.end >= range.end
2958 })
2959 });
2960
2961 if all_edits_within_excerpt {
2962 return Ok(());
2963 }
2964 }
2965 }
2966 }
2967 } else {
2968 return Ok(());
2969 }
2970
2971 let mut ranges_to_highlight = Vec::new();
2972 let excerpt_buffer = cx.add_model(|cx| {
2973 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2974 for (buffer_handle, transaction) in &entries {
2975 let buffer = buffer_handle.read(cx);
2976 ranges_to_highlight.extend(
2977 multibuffer.push_excerpts_with_context_lines(
2978 buffer_handle.clone(),
2979 buffer
2980 .edited_ranges_for_transaction::<usize>(transaction)
2981 .collect(),
2982 1,
2983 cx,
2984 ),
2985 );
2986 }
2987 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
2988 multibuffer
2989 });
2990
2991 workspace.update(&mut cx, |workspace, cx| {
2992 let project = workspace.project().clone();
2993 let editor =
2994 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2995 workspace.add_item(Box::new(editor.clone()), cx);
2996 editor.update(cx, |editor, cx| {
2997 editor.highlight_background::<Self>(
2998 ranges_to_highlight,
2999 |theme| theme.editor.highlighted_line_background,
3000 cx,
3001 );
3002 });
3003 })?;
3004
3005 Ok(())
3006 }
3007
3008 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3009 let project = self.project.as_ref()?;
3010 let buffer = self.buffer.read(cx);
3011 let newest_selection = self.selections.newest_anchor().clone();
3012 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3013 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3014 if start_buffer != end_buffer {
3015 return None;
3016 }
3017
3018 let actions = project.update(cx, |project, cx| {
3019 project.code_actions(&start_buffer, start..end, cx)
3020 });
3021 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3022 let actions = actions.await;
3023 this.update(&mut cx, |this, cx| {
3024 this.available_code_actions = actions.log_err().and_then(|actions| {
3025 if actions.is_empty() {
3026 None
3027 } else {
3028 Some((start_buffer, actions.into()))
3029 }
3030 });
3031 cx.notify();
3032 })
3033 .log_err();
3034 }));
3035 None
3036 }
3037
3038 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3039 if self.pending_rename.is_some() {
3040 return None;
3041 }
3042
3043 let project = self.project.as_ref()?;
3044 let buffer = self.buffer.read(cx);
3045 let newest_selection = self.selections.newest_anchor().clone();
3046 let cursor_position = newest_selection.head();
3047 let (cursor_buffer, cursor_buffer_position) =
3048 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3049 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3050 if cursor_buffer != tail_buffer {
3051 return None;
3052 }
3053
3054 let highlights = project.update(cx, |project, cx| {
3055 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3056 });
3057
3058 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3059 if let Some(highlights) = highlights.await.log_err() {
3060 this.update(&mut cx, |this, cx| {
3061 if this.pending_rename.is_some() {
3062 return;
3063 }
3064
3065 let buffer_id = cursor_position.buffer_id;
3066 let buffer = this.buffer.read(cx);
3067 if !buffer
3068 .text_anchor_for_position(cursor_position, cx)
3069 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3070 {
3071 return;
3072 }
3073
3074 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3075 let mut write_ranges = Vec::new();
3076 let mut read_ranges = Vec::new();
3077 for highlight in highlights {
3078 for (excerpt_id, excerpt_range) in
3079 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3080 {
3081 let start = highlight
3082 .range
3083 .start
3084 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3085 let end = highlight
3086 .range
3087 .end
3088 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3089 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3090 continue;
3091 }
3092
3093 let range = Anchor {
3094 buffer_id,
3095 excerpt_id: excerpt_id.clone(),
3096 text_anchor: start,
3097 }..Anchor {
3098 buffer_id,
3099 excerpt_id,
3100 text_anchor: end,
3101 };
3102 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3103 write_ranges.push(range);
3104 } else {
3105 read_ranges.push(range);
3106 }
3107 }
3108 }
3109
3110 this.highlight_background::<DocumentHighlightRead>(
3111 read_ranges,
3112 |theme| theme.editor.document_highlight_read_background,
3113 cx,
3114 );
3115 this.highlight_background::<DocumentHighlightWrite>(
3116 write_ranges,
3117 |theme| theme.editor.document_highlight_write_background,
3118 cx,
3119 );
3120 cx.notify();
3121 })
3122 .log_err();
3123 }
3124 }));
3125 None
3126 }
3127
3128 fn refresh_copilot_suggestions(
3129 &mut self,
3130 debounce: bool,
3131 cx: &mut ViewContext<Self>,
3132 ) -> Option<()> {
3133 let copilot = Copilot::global(cx)?;
3134 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3135 self.clear_copilot_suggestions(cx);
3136 return None;
3137 }
3138 self.update_visible_copilot_suggestion(cx);
3139
3140 let snapshot = self.buffer.read(cx).snapshot(cx);
3141 let cursor = self.selections.newest_anchor().head();
3142 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3143 self.clear_copilot_suggestions(cx);
3144 return None;
3145 }
3146
3147 let (buffer, buffer_position) =
3148 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3149 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3150 if debounce {
3151 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3152 }
3153
3154 let completions = copilot
3155 .update(&mut cx, |copilot, cx| {
3156 copilot.completions(&buffer, buffer_position, cx)
3157 })
3158 .await
3159 .log_err()
3160 .into_iter()
3161 .flatten()
3162 .collect_vec();
3163
3164 this.update(&mut cx, |this, cx| {
3165 if !completions.is_empty() {
3166 this.copilot_state.cycled = false;
3167 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3168 this.copilot_state.completions.clear();
3169 this.copilot_state.active_completion_index = 0;
3170 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3171 for completion in completions {
3172 this.copilot_state.push_completion(completion);
3173 }
3174 this.update_visible_copilot_suggestion(cx);
3175 }
3176 })
3177 .log_err()?;
3178 Some(())
3179 });
3180
3181 Some(())
3182 }
3183
3184 fn cycle_copilot_suggestions(
3185 &mut self,
3186 direction: Direction,
3187 cx: &mut ViewContext<Self>,
3188 ) -> Option<()> {
3189 let copilot = Copilot::global(cx)?;
3190 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3191 return None;
3192 }
3193
3194 if self.copilot_state.cycled {
3195 self.copilot_state.cycle_completions(direction);
3196 self.update_visible_copilot_suggestion(cx);
3197 } else {
3198 let cursor = self.selections.newest_anchor().head();
3199 let (buffer, buffer_position) =
3200 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3201 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3202 let completions = copilot
3203 .update(&mut cx, |copilot, cx| {
3204 copilot.completions_cycling(&buffer, buffer_position, cx)
3205 })
3206 .await;
3207
3208 this.update(&mut cx, |this, cx| {
3209 this.copilot_state.cycled = true;
3210 for completion in completions.log_err().into_iter().flatten() {
3211 this.copilot_state.push_completion(completion);
3212 }
3213 this.copilot_state.cycle_completions(direction);
3214 this.update_visible_copilot_suggestion(cx);
3215 })
3216 .log_err()?;
3217
3218 Some(())
3219 });
3220 }
3221
3222 Some(())
3223 }
3224
3225 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3226 if !self.has_active_copilot_suggestion(cx) {
3227 self.refresh_copilot_suggestions(false, cx);
3228 return;
3229 }
3230
3231 self.update_visible_copilot_suggestion(cx);
3232 }
3233
3234 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3235 if self.has_active_copilot_suggestion(cx) {
3236 self.cycle_copilot_suggestions(Direction::Next, cx);
3237 } else {
3238 self.refresh_copilot_suggestions(false, cx);
3239 }
3240 }
3241
3242 fn previous_copilot_suggestion(
3243 &mut self,
3244 _: &copilot::PreviousSuggestion,
3245 cx: &mut ViewContext<Self>,
3246 ) {
3247 if self.has_active_copilot_suggestion(cx) {
3248 self.cycle_copilot_suggestions(Direction::Prev, cx);
3249 } else {
3250 self.refresh_copilot_suggestions(false, cx);
3251 }
3252 }
3253
3254 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3255 if let Some(suggestion) = self
3256 .display_map
3257 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx))
3258 {
3259 if let Some((copilot, completion)) =
3260 Copilot::global(cx).zip(self.copilot_state.active_completion())
3261 {
3262 copilot
3263 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3264 .detach_and_log_err(cx);
3265
3266 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3267 }
3268 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3269 cx.notify();
3270 true
3271 } else {
3272 false
3273 }
3274 }
3275
3276 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3277 if self.has_active_copilot_suggestion(cx) {
3278 if let Some(copilot) = Copilot::global(cx) {
3279 copilot
3280 .update(cx, |copilot, cx| {
3281 copilot.discard_completions(&self.copilot_state.completions, cx)
3282 })
3283 .detach_and_log_err(cx);
3284
3285 self.report_copilot_event(None, false, cx)
3286 }
3287
3288 self.display_map
3289 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
3290 cx.notify();
3291 true
3292 } else {
3293 false
3294 }
3295 }
3296
3297 fn is_copilot_enabled_at(
3298 &self,
3299 location: Anchor,
3300 snapshot: &MultiBufferSnapshot,
3301 cx: &mut ViewContext<Self>,
3302 ) -> bool {
3303 let file = snapshot.file_at(location);
3304 let language = snapshot.language_at(location);
3305 let settings = all_language_settings(file, cx);
3306 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3307 }
3308
3309 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3310 self.display_map.read(cx).has_suggestion()
3311 }
3312
3313 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3314 let snapshot = self.buffer.read(cx).snapshot(cx);
3315 let selection = self.selections.newest_anchor();
3316 let cursor = selection.head();
3317
3318 if self.context_menu.is_some()
3319 || !self.completion_tasks.is_empty()
3320 || selection.start != selection.end
3321 {
3322 self.discard_copilot_suggestion(cx);
3323 } else if let Some(text) = self
3324 .copilot_state
3325 .text_for_active_completion(cursor, &snapshot)
3326 {
3327 self.display_map.update(cx, move |map, cx| {
3328 map.replace_suggestion(
3329 Some(Suggestion {
3330 position: cursor,
3331 text: text.trim_end().into(),
3332 }),
3333 cx,
3334 )
3335 });
3336 cx.notify();
3337 } else {
3338 self.discard_copilot_suggestion(cx);
3339 }
3340 }
3341
3342 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3343 self.copilot_state = Default::default();
3344 self.discard_copilot_suggestion(cx);
3345 }
3346
3347 pub fn render_code_actions_indicator(
3348 &self,
3349 style: &EditorStyle,
3350 is_active: bool,
3351 cx: &mut ViewContext<Self>,
3352 ) -> Option<AnyElement<Self>> {
3353 if self.available_code_actions.is_some() {
3354 enum CodeActions {}
3355 Some(
3356 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3357 Svg::new("icons/bolt_8.svg").with_color(
3358 style
3359 .code_actions
3360 .indicator
3361 .in_state(is_active)
3362 .style_for(state)
3363 .color,
3364 )
3365 })
3366 .with_cursor_style(CursorStyle::PointingHand)
3367 .with_padding(Padding::uniform(3.))
3368 .on_down(MouseButton::Left, |_, this, cx| {
3369 this.toggle_code_actions(
3370 &ToggleCodeActions {
3371 deployed_from_indicator: true,
3372 },
3373 cx,
3374 );
3375 })
3376 .into_any(),
3377 )
3378 } else {
3379 None
3380 }
3381 }
3382
3383 pub fn render_fold_indicators(
3384 &self,
3385 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3386 style: &EditorStyle,
3387 gutter_hovered: bool,
3388 line_height: f32,
3389 gutter_margin: f32,
3390 cx: &mut ViewContext<Self>,
3391 ) -> Vec<Option<AnyElement<Self>>> {
3392 enum FoldIndicators {}
3393
3394 let style = style.folds.clone();
3395
3396 fold_data
3397 .iter()
3398 .enumerate()
3399 .map(|(ix, fold_data)| {
3400 fold_data
3401 .map(|(fold_status, buffer_row, active)| {
3402 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3403 MouseEventHandler::<FoldIndicators, _>::new(
3404 ix as usize,
3405 cx,
3406 |mouse_state, _| {
3407 Svg::new(match fold_status {
3408 FoldStatus::Folded => style.folded_icon.clone(),
3409 FoldStatus::Foldable => style.foldable_icon.clone(),
3410 })
3411 .with_color(
3412 style
3413 .indicator
3414 .in_state(fold_status == FoldStatus::Folded)
3415 .style_for(mouse_state)
3416 .color,
3417 )
3418 .constrained()
3419 .with_width(gutter_margin * style.icon_margin_scale)
3420 .aligned()
3421 .constrained()
3422 .with_height(line_height)
3423 .with_width(gutter_margin)
3424 .aligned()
3425 },
3426 )
3427 .with_cursor_style(CursorStyle::PointingHand)
3428 .with_padding(Padding::uniform(3.))
3429 .on_click(MouseButton::Left, {
3430 move |_, editor, cx| match fold_status {
3431 FoldStatus::Folded => {
3432 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3433 }
3434 FoldStatus::Foldable => {
3435 editor.fold_at(&FoldAt { buffer_row }, cx);
3436 }
3437 }
3438 })
3439 .into_any()
3440 })
3441 })
3442 .flatten()
3443 })
3444 .collect()
3445 }
3446
3447 pub fn context_menu_visible(&self) -> bool {
3448 self.context_menu
3449 .as_ref()
3450 .map_or(false, |menu| menu.visible())
3451 }
3452
3453 pub fn render_context_menu(
3454 &self,
3455 cursor_position: DisplayPoint,
3456 style: EditorStyle,
3457 cx: &mut ViewContext<Editor>,
3458 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3459 self.context_menu
3460 .as_ref()
3461 .map(|menu| menu.render(cursor_position, style, cx))
3462 }
3463
3464 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3465 if !matches!(menu, ContextMenu::Completions(_)) {
3466 self.completion_tasks.clear();
3467 }
3468 self.context_menu = Some(menu);
3469 self.discard_copilot_suggestion(cx);
3470 cx.notify();
3471 }
3472
3473 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3474 cx.notify();
3475 self.completion_tasks.clear();
3476 let context_menu = self.context_menu.take();
3477 if context_menu.is_some() {
3478 self.update_visible_copilot_suggestion(cx);
3479 }
3480 context_menu
3481 }
3482
3483 pub fn insert_snippet(
3484 &mut self,
3485 insertion_ranges: &[Range<usize>],
3486 snippet: Snippet,
3487 cx: &mut ViewContext<Self>,
3488 ) -> Result<()> {
3489 let tabstops = self.buffer.update(cx, |buffer, cx| {
3490 let snippet_text: Arc<str> = snippet.text.clone().into();
3491 buffer.edit(
3492 insertion_ranges
3493 .iter()
3494 .cloned()
3495 .map(|range| (range, snippet_text.clone())),
3496 Some(AutoindentMode::EachLine),
3497 cx,
3498 );
3499
3500 let snapshot = &*buffer.read(cx);
3501 let snippet = &snippet;
3502 snippet
3503 .tabstops
3504 .iter()
3505 .map(|tabstop| {
3506 let mut tabstop_ranges = tabstop
3507 .iter()
3508 .flat_map(|tabstop_range| {
3509 let mut delta = 0_isize;
3510 insertion_ranges.iter().map(move |insertion_range| {
3511 let insertion_start = insertion_range.start as isize + delta;
3512 delta +=
3513 snippet.text.len() as isize - insertion_range.len() as isize;
3514
3515 let start = snapshot.anchor_before(
3516 (insertion_start + tabstop_range.start) as usize,
3517 );
3518 let end = snapshot
3519 .anchor_after((insertion_start + tabstop_range.end) as usize);
3520 start..end
3521 })
3522 })
3523 .collect::<Vec<_>>();
3524 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3525 tabstop_ranges
3526 })
3527 .collect::<Vec<_>>()
3528 });
3529
3530 if let Some(tabstop) = tabstops.first() {
3531 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3532 s.select_ranges(tabstop.iter().cloned());
3533 });
3534 self.snippet_stack.push(SnippetState {
3535 active_index: 0,
3536 ranges: tabstops,
3537 });
3538 }
3539
3540 Ok(())
3541 }
3542
3543 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3544 self.move_to_snippet_tabstop(Bias::Right, cx)
3545 }
3546
3547 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3548 self.move_to_snippet_tabstop(Bias::Left, cx)
3549 }
3550
3551 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3552 if let Some(mut snippet) = self.snippet_stack.pop() {
3553 match bias {
3554 Bias::Left => {
3555 if snippet.active_index > 0 {
3556 snippet.active_index -= 1;
3557 } else {
3558 self.snippet_stack.push(snippet);
3559 return false;
3560 }
3561 }
3562 Bias::Right => {
3563 if snippet.active_index + 1 < snippet.ranges.len() {
3564 snippet.active_index += 1;
3565 } else {
3566 self.snippet_stack.push(snippet);
3567 return false;
3568 }
3569 }
3570 }
3571 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3572 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3573 s.select_anchor_ranges(current_ranges.iter().cloned())
3574 });
3575 // If snippet state is not at the last tabstop, push it back on the stack
3576 if snippet.active_index + 1 < snippet.ranges.len() {
3577 self.snippet_stack.push(snippet);
3578 }
3579 return true;
3580 }
3581 }
3582
3583 false
3584 }
3585
3586 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3587 self.transact(cx, |this, cx| {
3588 this.select_all(&SelectAll, cx);
3589 this.insert("", cx);
3590 });
3591 }
3592
3593 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3594 self.transact(cx, |this, cx| {
3595 this.select_autoclose_pair(cx);
3596 let mut selections = this.selections.all::<Point>(cx);
3597 if !this.selections.line_mode {
3598 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3599 for selection in &mut selections {
3600 if selection.is_empty() {
3601 let old_head = selection.head();
3602 let mut new_head =
3603 movement::left(&display_map, old_head.to_display_point(&display_map))
3604 .to_point(&display_map);
3605 if let Some((buffer, line_buffer_range)) = display_map
3606 .buffer_snapshot
3607 .buffer_line_for_row(old_head.row)
3608 {
3609 let indent_size =
3610 buffer.indent_size_for_line(line_buffer_range.start.row);
3611 let indent_len = match indent_size.kind {
3612 IndentKind::Space => {
3613 buffer.settings_at(line_buffer_range.start, cx).tab_size
3614 }
3615 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3616 };
3617 if old_head.column <= indent_size.len && old_head.column > 0 {
3618 let indent_len = indent_len.get();
3619 new_head = cmp::min(
3620 new_head,
3621 Point::new(
3622 old_head.row,
3623 ((old_head.column - 1) / indent_len) * indent_len,
3624 ),
3625 );
3626 }
3627 }
3628
3629 selection.set_head(new_head, SelectionGoal::None);
3630 }
3631 }
3632 }
3633
3634 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3635 this.insert("", cx);
3636 this.refresh_copilot_suggestions(true, cx);
3637 });
3638 }
3639
3640 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3641 self.transact(cx, |this, cx| {
3642 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3643 let line_mode = s.line_mode;
3644 s.move_with(|map, selection| {
3645 if selection.is_empty() && !line_mode {
3646 let cursor = movement::right(map, selection.head());
3647 selection.end = cursor;
3648 selection.reversed = true;
3649 selection.goal = SelectionGoal::None;
3650 }
3651 })
3652 });
3653 this.insert("", cx);
3654 this.refresh_copilot_suggestions(true, cx);
3655 });
3656 }
3657
3658 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3659 if self.move_to_prev_snippet_tabstop(cx) {
3660 return;
3661 }
3662
3663 self.outdent(&Outdent, cx);
3664 }
3665
3666 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3667 if self.move_to_next_snippet_tabstop(cx) {
3668 return;
3669 }
3670
3671 let mut selections = self.selections.all_adjusted(cx);
3672 let buffer = self.buffer.read(cx);
3673 let snapshot = buffer.snapshot(cx);
3674 let rows_iter = selections.iter().map(|s| s.head().row);
3675 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3676
3677 let mut edits = Vec::new();
3678 let mut prev_edited_row = 0;
3679 let mut row_delta = 0;
3680 for selection in &mut selections {
3681 if selection.start.row != prev_edited_row {
3682 row_delta = 0;
3683 }
3684 prev_edited_row = selection.end.row;
3685
3686 // If the selection is non-empty, then increase the indentation of the selected lines.
3687 if !selection.is_empty() {
3688 row_delta =
3689 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3690 continue;
3691 }
3692
3693 // If the selection is empty and the cursor is in the leading whitespace before the
3694 // suggested indentation, then auto-indent the line.
3695 let cursor = selection.head();
3696 let current_indent = snapshot.indent_size_for_line(cursor.row);
3697 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3698 if cursor.column < suggested_indent.len
3699 && cursor.column <= current_indent.len
3700 && current_indent.len <= suggested_indent.len
3701 {
3702 selection.start = Point::new(cursor.row, suggested_indent.len);
3703 selection.end = selection.start;
3704 if row_delta == 0 {
3705 edits.extend(Buffer::edit_for_indent_size_adjustment(
3706 cursor.row,
3707 current_indent,
3708 suggested_indent,
3709 ));
3710 row_delta = suggested_indent.len - current_indent.len;
3711 }
3712 continue;
3713 }
3714 }
3715
3716 // Accept copilot suggestion if there is only one selection and the cursor is not
3717 // in the leading whitespace.
3718 if self.selections.count() == 1
3719 && cursor.column >= current_indent.len
3720 && self.has_active_copilot_suggestion(cx)
3721 {
3722 self.accept_copilot_suggestion(cx);
3723 return;
3724 }
3725
3726 // Otherwise, insert a hard or soft tab.
3727 let settings = buffer.settings_at(cursor, cx);
3728 let tab_size = if settings.hard_tabs {
3729 IndentSize::tab()
3730 } else {
3731 let tab_size = settings.tab_size.get();
3732 let char_column = snapshot
3733 .text_for_range(Point::new(cursor.row, 0)..cursor)
3734 .flat_map(str::chars)
3735 .count()
3736 + row_delta as usize;
3737 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3738 IndentSize::spaces(chars_to_next_tab_stop)
3739 };
3740 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3741 selection.end = selection.start;
3742 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3743 row_delta += tab_size.len;
3744 }
3745
3746 self.transact(cx, |this, cx| {
3747 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3748 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3749 this.refresh_copilot_suggestions(true, cx);
3750 });
3751 }
3752
3753 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3754 let mut selections = self.selections.all::<Point>(cx);
3755 let mut prev_edited_row = 0;
3756 let mut row_delta = 0;
3757 let mut edits = Vec::new();
3758 let buffer = self.buffer.read(cx);
3759 let snapshot = buffer.snapshot(cx);
3760 for selection in &mut selections {
3761 if selection.start.row != prev_edited_row {
3762 row_delta = 0;
3763 }
3764 prev_edited_row = selection.end.row;
3765
3766 row_delta =
3767 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3768 }
3769
3770 self.transact(cx, |this, cx| {
3771 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3772 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3773 });
3774 }
3775
3776 fn indent_selection(
3777 buffer: &MultiBuffer,
3778 snapshot: &MultiBufferSnapshot,
3779 selection: &mut Selection<Point>,
3780 edits: &mut Vec<(Range<Point>, String)>,
3781 delta_for_start_row: u32,
3782 cx: &AppContext,
3783 ) -> u32 {
3784 let settings = buffer.settings_at(selection.start, cx);
3785 let tab_size = settings.tab_size.get();
3786 let indent_kind = if settings.hard_tabs {
3787 IndentKind::Tab
3788 } else {
3789 IndentKind::Space
3790 };
3791 let mut start_row = selection.start.row;
3792 let mut end_row = selection.end.row + 1;
3793
3794 // If a selection ends at the beginning of a line, don't indent
3795 // that last line.
3796 if selection.end.column == 0 {
3797 end_row -= 1;
3798 }
3799
3800 // Avoid re-indenting a row that has already been indented by a
3801 // previous selection, but still update this selection's column
3802 // to reflect that indentation.
3803 if delta_for_start_row > 0 {
3804 start_row += 1;
3805 selection.start.column += delta_for_start_row;
3806 if selection.end.row == selection.start.row {
3807 selection.end.column += delta_for_start_row;
3808 }
3809 }
3810
3811 let mut delta_for_end_row = 0;
3812 for row in start_row..end_row {
3813 let current_indent = snapshot.indent_size_for_line(row);
3814 let indent_delta = match (current_indent.kind, indent_kind) {
3815 (IndentKind::Space, IndentKind::Space) => {
3816 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3817 IndentSize::spaces(columns_to_next_tab_stop)
3818 }
3819 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3820 (_, IndentKind::Tab) => IndentSize::tab(),
3821 };
3822
3823 let row_start = Point::new(row, 0);
3824 edits.push((
3825 row_start..row_start,
3826 indent_delta.chars().collect::<String>(),
3827 ));
3828
3829 // Update this selection's endpoints to reflect the indentation.
3830 if row == selection.start.row {
3831 selection.start.column += indent_delta.len;
3832 }
3833 if row == selection.end.row {
3834 selection.end.column += indent_delta.len;
3835 delta_for_end_row = indent_delta.len;
3836 }
3837 }
3838
3839 if selection.start.row == selection.end.row {
3840 delta_for_start_row + delta_for_end_row
3841 } else {
3842 delta_for_end_row
3843 }
3844 }
3845
3846 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3847 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3848 let selections = self.selections.all::<Point>(cx);
3849 let mut deletion_ranges = Vec::new();
3850 let mut last_outdent = None;
3851 {
3852 let buffer = self.buffer.read(cx);
3853 let snapshot = buffer.snapshot(cx);
3854 for selection in &selections {
3855 let settings = buffer.settings_at(selection.start, cx);
3856 let tab_size = settings.tab_size.get();
3857 let mut rows = selection.spanned_rows(false, &display_map);
3858
3859 // Avoid re-outdenting a row that has already been outdented by a
3860 // previous selection.
3861 if let Some(last_row) = last_outdent {
3862 if last_row == rows.start {
3863 rows.start += 1;
3864 }
3865 }
3866
3867 for row in rows {
3868 let indent_size = snapshot.indent_size_for_line(row);
3869 if indent_size.len > 0 {
3870 let deletion_len = match indent_size.kind {
3871 IndentKind::Space => {
3872 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3873 if columns_to_prev_tab_stop == 0 {
3874 tab_size
3875 } else {
3876 columns_to_prev_tab_stop
3877 }
3878 }
3879 IndentKind::Tab => 1,
3880 };
3881 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3882 last_outdent = Some(row);
3883 }
3884 }
3885 }
3886 }
3887
3888 self.transact(cx, |this, cx| {
3889 this.buffer.update(cx, |buffer, cx| {
3890 let empty_str: Arc<str> = "".into();
3891 buffer.edit(
3892 deletion_ranges
3893 .into_iter()
3894 .map(|range| (range, empty_str.clone())),
3895 None,
3896 cx,
3897 );
3898 });
3899 let selections = this.selections.all::<usize>(cx);
3900 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3901 });
3902 }
3903
3904 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3905 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3906 let selections = self.selections.all::<Point>(cx);
3907
3908 let mut new_cursors = Vec::new();
3909 let mut edit_ranges = Vec::new();
3910 let mut selections = selections.iter().peekable();
3911 while let Some(selection) = selections.next() {
3912 let mut rows = selection.spanned_rows(false, &display_map);
3913 let goal_display_column = selection.head().to_display_point(&display_map).column();
3914
3915 // Accumulate contiguous regions of rows that we want to delete.
3916 while let Some(next_selection) = selections.peek() {
3917 let next_rows = next_selection.spanned_rows(false, &display_map);
3918 if next_rows.start <= rows.end {
3919 rows.end = next_rows.end;
3920 selections.next().unwrap();
3921 } else {
3922 break;
3923 }
3924 }
3925
3926 let buffer = &display_map.buffer_snapshot;
3927 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3928 let edit_end;
3929 let cursor_buffer_row;
3930 if buffer.max_point().row >= rows.end {
3931 // If there's a line after the range, delete the \n from the end of the row range
3932 // and position the cursor on the next line.
3933 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3934 cursor_buffer_row = rows.end;
3935 } else {
3936 // If there isn't a line after the range, delete the \n from the line before the
3937 // start of the row range and position the cursor there.
3938 edit_start = edit_start.saturating_sub(1);
3939 edit_end = buffer.len();
3940 cursor_buffer_row = rows.start.saturating_sub(1);
3941 }
3942
3943 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3944 *cursor.column_mut() =
3945 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3946
3947 new_cursors.push((
3948 selection.id,
3949 buffer.anchor_after(cursor.to_point(&display_map)),
3950 ));
3951 edit_ranges.push(edit_start..edit_end);
3952 }
3953
3954 self.transact(cx, |this, cx| {
3955 let buffer = this.buffer.update(cx, |buffer, cx| {
3956 let empty_str: Arc<str> = "".into();
3957 buffer.edit(
3958 edit_ranges
3959 .into_iter()
3960 .map(|range| (range, empty_str.clone())),
3961 None,
3962 cx,
3963 );
3964 buffer.snapshot(cx)
3965 });
3966 let new_selections = new_cursors
3967 .into_iter()
3968 .map(|(id, cursor)| {
3969 let cursor = cursor.to_point(&buffer);
3970 Selection {
3971 id,
3972 start: cursor,
3973 end: cursor,
3974 reversed: false,
3975 goal: SelectionGoal::None,
3976 }
3977 })
3978 .collect();
3979
3980 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3981 s.select(new_selections);
3982 });
3983 });
3984 }
3985
3986 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
3987 let mut row_ranges = Vec::<Range<u32>>::new();
3988 for selection in self.selections.all::<Point>(cx) {
3989 let start = selection.start.row;
3990 let end = if selection.start.row == selection.end.row {
3991 selection.start.row + 1
3992 } else {
3993 selection.end.row
3994 };
3995
3996 if let Some(last_row_range) = row_ranges.last_mut() {
3997 if start <= last_row_range.end {
3998 last_row_range.end = end;
3999 continue;
4000 }
4001 }
4002 row_ranges.push(start..end);
4003 }
4004
4005 let snapshot = self.buffer.read(cx).snapshot(cx);
4006 let mut cursor_positions = Vec::new();
4007 for row_range in &row_ranges {
4008 let anchor = snapshot.anchor_before(Point::new(
4009 row_range.end - 1,
4010 snapshot.line_len(row_range.end - 1),
4011 ));
4012 cursor_positions.push(anchor.clone()..anchor);
4013 }
4014
4015 self.transact(cx, |this, cx| {
4016 for row_range in row_ranges.into_iter().rev() {
4017 for row in row_range.rev() {
4018 let end_of_line = Point::new(row, snapshot.line_len(row));
4019 let indent = snapshot.indent_size_for_line(row + 1);
4020 let start_of_next_line = Point::new(row + 1, indent.len);
4021
4022 let replace = if snapshot.line_len(row + 1) > indent.len {
4023 " "
4024 } else {
4025 ""
4026 };
4027
4028 this.buffer.update(cx, |buffer, cx| {
4029 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4030 });
4031 }
4032 }
4033
4034 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4035 s.select_anchor_ranges(cursor_positions)
4036 });
4037 });
4038 }
4039
4040 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4041 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4042 let buffer = &display_map.buffer_snapshot;
4043 let selections = self.selections.all::<Point>(cx);
4044
4045 let mut edits = Vec::new();
4046 let mut selections_iter = selections.iter().peekable();
4047 while let Some(selection) = selections_iter.next() {
4048 // Avoid duplicating the same lines twice.
4049 let mut rows = selection.spanned_rows(false, &display_map);
4050
4051 while let Some(next_selection) = selections_iter.peek() {
4052 let next_rows = next_selection.spanned_rows(false, &display_map);
4053 if next_rows.start < rows.end {
4054 rows.end = next_rows.end;
4055 selections_iter.next().unwrap();
4056 } else {
4057 break;
4058 }
4059 }
4060
4061 // Copy the text from the selected row region and splice it at the start of the region.
4062 let start = Point::new(rows.start, 0);
4063 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4064 let text = buffer
4065 .text_for_range(start..end)
4066 .chain(Some("\n"))
4067 .collect::<String>();
4068 edits.push((start..start, text));
4069 }
4070
4071 self.transact(cx, |this, cx| {
4072 this.buffer.update(cx, |buffer, cx| {
4073 buffer.edit(edits, None, cx);
4074 });
4075
4076 this.request_autoscroll(Autoscroll::fit(), cx);
4077 });
4078 }
4079
4080 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4081 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4082 let buffer = self.buffer.read(cx).snapshot(cx);
4083
4084 let mut edits = Vec::new();
4085 let mut unfold_ranges = Vec::new();
4086 let mut refold_ranges = Vec::new();
4087
4088 let selections = self.selections.all::<Point>(cx);
4089 let mut selections = selections.iter().peekable();
4090 let mut contiguous_row_selections = Vec::new();
4091 let mut new_selections = Vec::new();
4092
4093 while let Some(selection) = selections.next() {
4094 // Find all the selections that span a contiguous row range
4095 let (start_row, end_row) = consume_contiguous_rows(
4096 &mut contiguous_row_selections,
4097 selection,
4098 &display_map,
4099 &mut selections,
4100 );
4101
4102 // Move the text spanned by the row range to be before the line preceding the row range
4103 if start_row > 0 {
4104 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4105 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4106 let insertion_point = display_map
4107 .prev_line_boundary(Point::new(start_row - 1, 0))
4108 .0;
4109
4110 // Don't move lines across excerpts
4111 if buffer
4112 .excerpt_boundaries_in_range((
4113 Bound::Excluded(insertion_point),
4114 Bound::Included(range_to_move.end),
4115 ))
4116 .next()
4117 .is_none()
4118 {
4119 let text = buffer
4120 .text_for_range(range_to_move.clone())
4121 .flat_map(|s| s.chars())
4122 .skip(1)
4123 .chain(['\n'])
4124 .collect::<String>();
4125
4126 edits.push((
4127 buffer.anchor_after(range_to_move.start)
4128 ..buffer.anchor_before(range_to_move.end),
4129 String::new(),
4130 ));
4131 let insertion_anchor = buffer.anchor_after(insertion_point);
4132 edits.push((insertion_anchor..insertion_anchor, text));
4133
4134 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4135
4136 // Move selections up
4137 new_selections.extend(contiguous_row_selections.drain(..).map(
4138 |mut selection| {
4139 selection.start.row -= row_delta;
4140 selection.end.row -= row_delta;
4141 selection
4142 },
4143 ));
4144
4145 // Move folds up
4146 unfold_ranges.push(range_to_move.clone());
4147 for fold in display_map.folds_in_range(
4148 buffer.anchor_before(range_to_move.start)
4149 ..buffer.anchor_after(range_to_move.end),
4150 ) {
4151 let mut start = fold.start.to_point(&buffer);
4152 let mut end = fold.end.to_point(&buffer);
4153 start.row -= row_delta;
4154 end.row -= row_delta;
4155 refold_ranges.push(start..end);
4156 }
4157 }
4158 }
4159
4160 // If we didn't move line(s), preserve the existing selections
4161 new_selections.append(&mut contiguous_row_selections);
4162 }
4163
4164 self.transact(cx, |this, cx| {
4165 this.unfold_ranges(unfold_ranges, true, true, cx);
4166 this.buffer.update(cx, |buffer, cx| {
4167 for (range, text) in edits {
4168 buffer.edit([(range, text)], None, cx);
4169 }
4170 });
4171 this.fold_ranges(refold_ranges, true, cx);
4172 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4173 s.select(new_selections);
4174 })
4175 });
4176 }
4177
4178 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4179 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4180 let buffer = self.buffer.read(cx).snapshot(cx);
4181
4182 let mut edits = Vec::new();
4183 let mut unfold_ranges = Vec::new();
4184 let mut refold_ranges = Vec::new();
4185
4186 let selections = self.selections.all::<Point>(cx);
4187 let mut selections = selections.iter().peekable();
4188 let mut contiguous_row_selections = Vec::new();
4189 let mut new_selections = Vec::new();
4190
4191 while let Some(selection) = selections.next() {
4192 // Find all the selections that span a contiguous row range
4193 let (start_row, end_row) = consume_contiguous_rows(
4194 &mut contiguous_row_selections,
4195 selection,
4196 &display_map,
4197 &mut selections,
4198 );
4199
4200 // Move the text spanned by the row range to be after the last line of the row range
4201 if end_row <= buffer.max_point().row {
4202 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4203 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4204
4205 // Don't move lines across excerpt boundaries
4206 if buffer
4207 .excerpt_boundaries_in_range((
4208 Bound::Excluded(range_to_move.start),
4209 Bound::Included(insertion_point),
4210 ))
4211 .next()
4212 .is_none()
4213 {
4214 let mut text = String::from("\n");
4215 text.extend(buffer.text_for_range(range_to_move.clone()));
4216 text.pop(); // Drop trailing newline
4217 edits.push((
4218 buffer.anchor_after(range_to_move.start)
4219 ..buffer.anchor_before(range_to_move.end),
4220 String::new(),
4221 ));
4222 let insertion_anchor = buffer.anchor_after(insertion_point);
4223 edits.push((insertion_anchor..insertion_anchor, text));
4224
4225 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4226
4227 // Move selections down
4228 new_selections.extend(contiguous_row_selections.drain(..).map(
4229 |mut selection| {
4230 selection.start.row += row_delta;
4231 selection.end.row += row_delta;
4232 selection
4233 },
4234 ));
4235
4236 // Move folds down
4237 unfold_ranges.push(range_to_move.clone());
4238 for fold in display_map.folds_in_range(
4239 buffer.anchor_before(range_to_move.start)
4240 ..buffer.anchor_after(range_to_move.end),
4241 ) {
4242 let mut start = fold.start.to_point(&buffer);
4243 let mut end = fold.end.to_point(&buffer);
4244 start.row += row_delta;
4245 end.row += row_delta;
4246 refold_ranges.push(start..end);
4247 }
4248 }
4249 }
4250
4251 // If we didn't move line(s), preserve the existing selections
4252 new_selections.append(&mut contiguous_row_selections);
4253 }
4254
4255 self.transact(cx, |this, cx| {
4256 this.unfold_ranges(unfold_ranges, true, true, cx);
4257 this.buffer.update(cx, |buffer, cx| {
4258 for (range, text) in edits {
4259 buffer.edit([(range, text)], None, cx);
4260 }
4261 });
4262 this.fold_ranges(refold_ranges, true, cx);
4263 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4264 });
4265 }
4266
4267 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4268 self.transact(cx, |this, cx| {
4269 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4270 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4271 let line_mode = s.line_mode;
4272 s.move_with(|display_map, selection| {
4273 if !selection.is_empty() || line_mode {
4274 return;
4275 }
4276
4277 let mut head = selection.head();
4278 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4279 if head.column() == display_map.line_len(head.row()) {
4280 transpose_offset = display_map
4281 .buffer_snapshot
4282 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4283 }
4284
4285 if transpose_offset == 0 {
4286 return;
4287 }
4288
4289 *head.column_mut() += 1;
4290 head = display_map.clip_point(head, Bias::Right);
4291 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4292
4293 let transpose_start = display_map
4294 .buffer_snapshot
4295 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4296 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4297 let transpose_end = display_map
4298 .buffer_snapshot
4299 .clip_offset(transpose_offset + 1, Bias::Right);
4300 if let Some(ch) =
4301 display_map.buffer_snapshot.chars_at(transpose_start).next()
4302 {
4303 edits.push((transpose_start..transpose_offset, String::new()));
4304 edits.push((transpose_end..transpose_end, ch.to_string()));
4305 }
4306 }
4307 });
4308 edits
4309 });
4310 this.buffer
4311 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4312 let selections = this.selections.all::<usize>(cx);
4313 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4314 s.select(selections);
4315 });
4316 });
4317 }
4318
4319 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4320 let mut text = String::new();
4321 let buffer = self.buffer.read(cx).snapshot(cx);
4322 let mut selections = self.selections.all::<Point>(cx);
4323 let mut clipboard_selections = Vec::with_capacity(selections.len());
4324 {
4325 let max_point = buffer.max_point();
4326 for selection in &mut selections {
4327 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4328 if is_entire_line {
4329 selection.start = Point::new(selection.start.row, 0);
4330 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4331 selection.goal = SelectionGoal::None;
4332 }
4333 let mut len = 0;
4334 for chunk in buffer.text_for_range(selection.start..selection.end) {
4335 text.push_str(chunk);
4336 len += chunk.len();
4337 }
4338 clipboard_selections.push(ClipboardSelection {
4339 len,
4340 is_entire_line,
4341 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4342 });
4343 }
4344 }
4345
4346 self.transact(cx, |this, cx| {
4347 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4348 s.select(selections);
4349 });
4350 this.insert("", cx);
4351 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4352 });
4353 }
4354
4355 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4356 let selections = self.selections.all::<Point>(cx);
4357 let buffer = self.buffer.read(cx).read(cx);
4358 let mut text = String::new();
4359
4360 let mut clipboard_selections = Vec::with_capacity(selections.len());
4361 {
4362 let max_point = buffer.max_point();
4363 for selection in selections.iter() {
4364 let mut start = selection.start;
4365 let mut end = selection.end;
4366 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4367 if is_entire_line {
4368 start = Point::new(start.row, 0);
4369 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4370 }
4371 let mut len = 0;
4372 for chunk in buffer.text_for_range(start..end) {
4373 text.push_str(chunk);
4374 len += chunk.len();
4375 }
4376 clipboard_selections.push(ClipboardSelection {
4377 len,
4378 is_entire_line,
4379 first_line_indent: buffer.indent_size_for_line(start.row).len,
4380 });
4381 }
4382 }
4383
4384 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4385 }
4386
4387 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4388 self.transact(cx, |this, cx| {
4389 if let Some(item) = cx.read_from_clipboard() {
4390 let mut clipboard_text = Cow::Borrowed(item.text());
4391 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4392 let old_selections = this.selections.all::<usize>(cx);
4393 let all_selections_were_entire_line =
4394 clipboard_selections.iter().all(|s| s.is_entire_line);
4395 let first_selection_indent_column =
4396 clipboard_selections.first().map(|s| s.first_line_indent);
4397 if clipboard_selections.len() != old_selections.len() {
4398 let mut newline_separated_text = String::new();
4399 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4400 let mut ix = 0;
4401 while let Some(clipboard_selection) = clipboard_selections.next() {
4402 newline_separated_text
4403 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4404 ix += clipboard_selection.len;
4405 if clipboard_selections.peek().is_some() {
4406 newline_separated_text.push('\n');
4407 }
4408 }
4409 clipboard_text = Cow::Owned(newline_separated_text);
4410 }
4411
4412 this.buffer.update(cx, |buffer, cx| {
4413 let snapshot = buffer.read(cx);
4414 let mut start_offset = 0;
4415 let mut edits = Vec::new();
4416 let mut original_indent_columns = Vec::new();
4417 let line_mode = this.selections.line_mode;
4418 for (ix, selection) in old_selections.iter().enumerate() {
4419 let to_insert;
4420 let entire_line;
4421 let original_indent_column;
4422 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4423 let end_offset = start_offset + clipboard_selection.len;
4424 to_insert = &clipboard_text[start_offset..end_offset];
4425 entire_line = clipboard_selection.is_entire_line;
4426 start_offset = end_offset;
4427 original_indent_column =
4428 Some(clipboard_selection.first_line_indent);
4429 } else {
4430 to_insert = clipboard_text.as_str();
4431 entire_line = all_selections_were_entire_line;
4432 original_indent_column = first_selection_indent_column
4433 }
4434
4435 // If the corresponding selection was empty when this slice of the
4436 // clipboard text was written, then the entire line containing the
4437 // selection was copied. If this selection is also currently empty,
4438 // then paste the line before the current line of the buffer.
4439 let range = if selection.is_empty() && !line_mode && entire_line {
4440 let column = selection.start.to_point(&snapshot).column as usize;
4441 let line_start = selection.start - column;
4442 line_start..line_start
4443 } else {
4444 selection.range()
4445 };
4446
4447 edits.push((range, to_insert));
4448 original_indent_columns.extend(original_indent_column);
4449 }
4450 drop(snapshot);
4451
4452 buffer.edit(
4453 edits,
4454 Some(AutoindentMode::Block {
4455 original_indent_columns,
4456 }),
4457 cx,
4458 );
4459 });
4460
4461 let selections = this.selections.all::<usize>(cx);
4462 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4463 } else {
4464 this.insert(&clipboard_text, cx);
4465 }
4466 }
4467 });
4468 }
4469
4470 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4471 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4472 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4473 self.change_selections(None, cx, |s| {
4474 s.select_anchors(selections.to_vec());
4475 });
4476 }
4477 self.request_autoscroll(Autoscroll::fit(), cx);
4478 self.unmark_text(cx);
4479 self.refresh_copilot_suggestions(true, cx);
4480 cx.emit(Event::Edited);
4481 }
4482 }
4483
4484 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4485 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4486 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4487 {
4488 self.change_selections(None, cx, |s| {
4489 s.select_anchors(selections.to_vec());
4490 });
4491 }
4492 self.request_autoscroll(Autoscroll::fit(), cx);
4493 self.unmark_text(cx);
4494 self.refresh_copilot_suggestions(true, cx);
4495 cx.emit(Event::Edited);
4496 }
4497 }
4498
4499 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4500 self.buffer
4501 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4502 }
4503
4504 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4505 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4506 let line_mode = s.line_mode;
4507 s.move_with(|map, selection| {
4508 let cursor = if selection.is_empty() && !line_mode {
4509 movement::left(map, selection.start)
4510 } else {
4511 selection.start
4512 };
4513 selection.collapse_to(cursor, SelectionGoal::None);
4514 });
4515 })
4516 }
4517
4518 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4519 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4520 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4521 })
4522 }
4523
4524 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4525 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4526 let line_mode = s.line_mode;
4527 s.move_with(|map, selection| {
4528 let cursor = if selection.is_empty() && !line_mode {
4529 movement::right(map, selection.end)
4530 } else {
4531 selection.end
4532 };
4533 selection.collapse_to(cursor, SelectionGoal::None)
4534 });
4535 })
4536 }
4537
4538 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4539 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4540 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4541 })
4542 }
4543
4544 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4545 if self.take_rename(true, cx).is_some() {
4546 return;
4547 }
4548
4549 if let Some(context_menu) = self.context_menu.as_mut() {
4550 if context_menu.select_prev(cx) {
4551 return;
4552 }
4553 }
4554
4555 if matches!(self.mode, EditorMode::SingleLine) {
4556 cx.propagate_action();
4557 return;
4558 }
4559
4560 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4561 let line_mode = s.line_mode;
4562 s.move_with(|map, selection| {
4563 if !selection.is_empty() && !line_mode {
4564 selection.goal = SelectionGoal::None;
4565 }
4566 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4567 selection.collapse_to(cursor, goal);
4568 });
4569 })
4570 }
4571
4572 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4573 if self.take_rename(true, cx).is_some() {
4574 return;
4575 }
4576
4577 if self
4578 .context_menu
4579 .as_mut()
4580 .map(|menu| menu.select_first(cx))
4581 .unwrap_or(false)
4582 {
4583 return;
4584 }
4585
4586 if matches!(self.mode, EditorMode::SingleLine) {
4587 cx.propagate_action();
4588 return;
4589 }
4590
4591 let row_count = if let Some(row_count) = self.visible_line_count() {
4592 row_count as u32 - 1
4593 } else {
4594 return;
4595 };
4596
4597 let autoscroll = if action.center_cursor {
4598 Autoscroll::center()
4599 } else {
4600 Autoscroll::fit()
4601 };
4602
4603 self.change_selections(Some(autoscroll), cx, |s| {
4604 let line_mode = s.line_mode;
4605 s.move_with(|map, selection| {
4606 if !selection.is_empty() && !line_mode {
4607 selection.goal = SelectionGoal::None;
4608 }
4609 let (cursor, goal) =
4610 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4611 selection.collapse_to(cursor, goal);
4612 });
4613 });
4614 }
4615
4616 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4617 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4618 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4619 })
4620 }
4621
4622 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4623 self.take_rename(true, cx);
4624
4625 if let Some(context_menu) = self.context_menu.as_mut() {
4626 if context_menu.select_next(cx) {
4627 return;
4628 }
4629 }
4630
4631 if self.mode == EditorMode::SingleLine {
4632 cx.propagate_action();
4633 return;
4634 }
4635
4636 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4637 let line_mode = s.line_mode;
4638 s.move_with(|map, selection| {
4639 if !selection.is_empty() && !line_mode {
4640 selection.goal = SelectionGoal::None;
4641 }
4642 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4643 selection.collapse_to(cursor, goal);
4644 });
4645 });
4646 }
4647
4648 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4649 if self.take_rename(true, cx).is_some() {
4650 return;
4651 }
4652
4653 if self
4654 .context_menu
4655 .as_mut()
4656 .map(|menu| menu.select_last(cx))
4657 .unwrap_or(false)
4658 {
4659 return;
4660 }
4661
4662 if matches!(self.mode, EditorMode::SingleLine) {
4663 cx.propagate_action();
4664 return;
4665 }
4666
4667 let row_count = if let Some(row_count) = self.visible_line_count() {
4668 row_count as u32 - 1
4669 } else {
4670 return;
4671 };
4672
4673 let autoscroll = if action.center_cursor {
4674 Autoscroll::center()
4675 } else {
4676 Autoscroll::fit()
4677 };
4678
4679 self.change_selections(Some(autoscroll), cx, |s| {
4680 let line_mode = s.line_mode;
4681 s.move_with(|map, selection| {
4682 if !selection.is_empty() && !line_mode {
4683 selection.goal = SelectionGoal::None;
4684 }
4685 let (cursor, goal) =
4686 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4687 selection.collapse_to(cursor, goal);
4688 });
4689 });
4690 }
4691
4692 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4693 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4694 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4695 });
4696 }
4697
4698 pub fn move_to_previous_word_start(
4699 &mut self,
4700 _: &MoveToPreviousWordStart,
4701 cx: &mut ViewContext<Self>,
4702 ) {
4703 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4704 s.move_cursors_with(|map, head, _| {
4705 (
4706 movement::previous_word_start(map, head),
4707 SelectionGoal::None,
4708 )
4709 });
4710 })
4711 }
4712
4713 pub fn move_to_previous_subword_start(
4714 &mut self,
4715 _: &MoveToPreviousSubwordStart,
4716 cx: &mut ViewContext<Self>,
4717 ) {
4718 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4719 s.move_cursors_with(|map, head, _| {
4720 (
4721 movement::previous_subword_start(map, head),
4722 SelectionGoal::None,
4723 )
4724 });
4725 })
4726 }
4727
4728 pub fn select_to_previous_word_start(
4729 &mut self,
4730 _: &SelectToPreviousWordStart,
4731 cx: &mut ViewContext<Self>,
4732 ) {
4733 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4734 s.move_heads_with(|map, head, _| {
4735 (
4736 movement::previous_word_start(map, head),
4737 SelectionGoal::None,
4738 )
4739 });
4740 })
4741 }
4742
4743 pub fn select_to_previous_subword_start(
4744 &mut self,
4745 _: &SelectToPreviousSubwordStart,
4746 cx: &mut ViewContext<Self>,
4747 ) {
4748 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4749 s.move_heads_with(|map, head, _| {
4750 (
4751 movement::previous_subword_start(map, head),
4752 SelectionGoal::None,
4753 )
4754 });
4755 })
4756 }
4757
4758 pub fn delete_to_previous_word_start(
4759 &mut self,
4760 _: &DeleteToPreviousWordStart,
4761 cx: &mut ViewContext<Self>,
4762 ) {
4763 self.transact(cx, |this, cx| {
4764 this.select_autoclose_pair(cx);
4765 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4766 let line_mode = s.line_mode;
4767 s.move_with(|map, selection| {
4768 if selection.is_empty() && !line_mode {
4769 let cursor = movement::previous_word_start(map, selection.head());
4770 selection.set_head(cursor, SelectionGoal::None);
4771 }
4772 });
4773 });
4774 this.insert("", cx);
4775 });
4776 }
4777
4778 pub fn delete_to_previous_subword_start(
4779 &mut self,
4780 _: &DeleteToPreviousSubwordStart,
4781 cx: &mut ViewContext<Self>,
4782 ) {
4783 self.transact(cx, |this, cx| {
4784 this.select_autoclose_pair(cx);
4785 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4786 let line_mode = s.line_mode;
4787 s.move_with(|map, selection| {
4788 if selection.is_empty() && !line_mode {
4789 let cursor = movement::previous_subword_start(map, selection.head());
4790 selection.set_head(cursor, SelectionGoal::None);
4791 }
4792 });
4793 });
4794 this.insert("", cx);
4795 });
4796 }
4797
4798 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4799 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4800 s.move_cursors_with(|map, head, _| {
4801 (movement::next_word_end(map, head), SelectionGoal::None)
4802 });
4803 })
4804 }
4805
4806 pub fn move_to_next_subword_end(
4807 &mut self,
4808 _: &MoveToNextSubwordEnd,
4809 cx: &mut ViewContext<Self>,
4810 ) {
4811 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4812 s.move_cursors_with(|map, head, _| {
4813 (movement::next_subword_end(map, head), SelectionGoal::None)
4814 });
4815 })
4816 }
4817
4818 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4819 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4820 s.move_heads_with(|map, head, _| {
4821 (movement::next_word_end(map, head), SelectionGoal::None)
4822 });
4823 })
4824 }
4825
4826 pub fn select_to_next_subword_end(
4827 &mut self,
4828 _: &SelectToNextSubwordEnd,
4829 cx: &mut ViewContext<Self>,
4830 ) {
4831 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4832 s.move_heads_with(|map, head, _| {
4833 (movement::next_subword_end(map, head), SelectionGoal::None)
4834 });
4835 })
4836 }
4837
4838 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4839 self.transact(cx, |this, cx| {
4840 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4841 let line_mode = s.line_mode;
4842 s.move_with(|map, selection| {
4843 if selection.is_empty() && !line_mode {
4844 let cursor = movement::next_word_end(map, selection.head());
4845 selection.set_head(cursor, SelectionGoal::None);
4846 }
4847 });
4848 });
4849 this.insert("", cx);
4850 });
4851 }
4852
4853 pub fn delete_to_next_subword_end(
4854 &mut self,
4855 _: &DeleteToNextSubwordEnd,
4856 cx: &mut ViewContext<Self>,
4857 ) {
4858 self.transact(cx, |this, cx| {
4859 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4860 s.move_with(|map, selection| {
4861 if selection.is_empty() {
4862 let cursor = movement::next_subword_end(map, selection.head());
4863 selection.set_head(cursor, SelectionGoal::None);
4864 }
4865 });
4866 });
4867 this.insert("", cx);
4868 });
4869 }
4870
4871 pub fn move_to_beginning_of_line(
4872 &mut self,
4873 _: &MoveToBeginningOfLine,
4874 cx: &mut ViewContext<Self>,
4875 ) {
4876 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4877 s.move_cursors_with(|map, head, _| {
4878 (
4879 movement::indented_line_beginning(map, head, true),
4880 SelectionGoal::None,
4881 )
4882 });
4883 })
4884 }
4885
4886 pub fn select_to_beginning_of_line(
4887 &mut self,
4888 action: &SelectToBeginningOfLine,
4889 cx: &mut ViewContext<Self>,
4890 ) {
4891 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4892 s.move_heads_with(|map, head, _| {
4893 (
4894 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4895 SelectionGoal::None,
4896 )
4897 });
4898 });
4899 }
4900
4901 pub fn delete_to_beginning_of_line(
4902 &mut self,
4903 _: &DeleteToBeginningOfLine,
4904 cx: &mut ViewContext<Self>,
4905 ) {
4906 self.transact(cx, |this, cx| {
4907 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4908 s.move_with(|_, selection| {
4909 selection.reversed = true;
4910 });
4911 });
4912
4913 this.select_to_beginning_of_line(
4914 &SelectToBeginningOfLine {
4915 stop_at_soft_wraps: false,
4916 },
4917 cx,
4918 );
4919 this.backspace(&Backspace, cx);
4920 });
4921 }
4922
4923 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4924 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4925 s.move_cursors_with(|map, head, _| {
4926 (movement::line_end(map, head, true), SelectionGoal::None)
4927 });
4928 })
4929 }
4930
4931 pub fn select_to_end_of_line(
4932 &mut self,
4933 action: &SelectToEndOfLine,
4934 cx: &mut ViewContext<Self>,
4935 ) {
4936 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4937 s.move_heads_with(|map, head, _| {
4938 (
4939 movement::line_end(map, head, action.stop_at_soft_wraps),
4940 SelectionGoal::None,
4941 )
4942 });
4943 })
4944 }
4945
4946 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4947 self.transact(cx, |this, cx| {
4948 this.select_to_end_of_line(
4949 &SelectToEndOfLine {
4950 stop_at_soft_wraps: false,
4951 },
4952 cx,
4953 );
4954 this.delete(&Delete, cx);
4955 });
4956 }
4957
4958 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4959 self.transact(cx, |this, cx| {
4960 this.select_to_end_of_line(
4961 &SelectToEndOfLine {
4962 stop_at_soft_wraps: false,
4963 },
4964 cx,
4965 );
4966 this.cut(&Cut, cx);
4967 });
4968 }
4969
4970 pub fn move_to_start_of_paragraph(
4971 &mut self,
4972 _: &MoveToStartOfParagraph,
4973 cx: &mut ViewContext<Self>,
4974 ) {
4975 if matches!(self.mode, EditorMode::SingleLine) {
4976 cx.propagate_action();
4977 return;
4978 }
4979
4980 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4981 s.move_with(|map, selection| {
4982 selection.collapse_to(
4983 movement::start_of_paragraph(map, selection.head()),
4984 SelectionGoal::None,
4985 )
4986 });
4987 })
4988 }
4989
4990 pub fn move_to_end_of_paragraph(
4991 &mut self,
4992 _: &MoveToEndOfParagraph,
4993 cx: &mut ViewContext<Self>,
4994 ) {
4995 if matches!(self.mode, EditorMode::SingleLine) {
4996 cx.propagate_action();
4997 return;
4998 }
4999
5000 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5001 s.move_with(|map, selection| {
5002 selection.collapse_to(
5003 movement::end_of_paragraph(map, selection.head()),
5004 SelectionGoal::None,
5005 )
5006 });
5007 })
5008 }
5009
5010 pub fn select_to_start_of_paragraph(
5011 &mut self,
5012 _: &SelectToStartOfParagraph,
5013 cx: &mut ViewContext<Self>,
5014 ) {
5015 if matches!(self.mode, EditorMode::SingleLine) {
5016 cx.propagate_action();
5017 return;
5018 }
5019
5020 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5021 s.move_heads_with(|map, head, _| {
5022 (movement::start_of_paragraph(map, head), SelectionGoal::None)
5023 });
5024 })
5025 }
5026
5027 pub fn select_to_end_of_paragraph(
5028 &mut self,
5029 _: &SelectToEndOfParagraph,
5030 cx: &mut ViewContext<Self>,
5031 ) {
5032 if matches!(self.mode, EditorMode::SingleLine) {
5033 cx.propagate_action();
5034 return;
5035 }
5036
5037 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5038 s.move_heads_with(|map, head, _| {
5039 (movement::end_of_paragraph(map, head), SelectionGoal::None)
5040 });
5041 })
5042 }
5043
5044 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5045 if matches!(self.mode, EditorMode::SingleLine) {
5046 cx.propagate_action();
5047 return;
5048 }
5049
5050 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5051 s.select_ranges(vec![0..0]);
5052 });
5053 }
5054
5055 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5056 let mut selection = self.selections.last::<Point>(cx);
5057 selection.set_head(Point::zero(), SelectionGoal::None);
5058
5059 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5060 s.select(vec![selection]);
5061 });
5062 }
5063
5064 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5065 if matches!(self.mode, EditorMode::SingleLine) {
5066 cx.propagate_action();
5067 return;
5068 }
5069
5070 let cursor = self.buffer.read(cx).read(cx).len();
5071 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5072 s.select_ranges(vec![cursor..cursor])
5073 });
5074 }
5075
5076 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5077 self.nav_history = nav_history;
5078 }
5079
5080 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5081 self.nav_history.as_ref()
5082 }
5083
5084 fn push_to_nav_history(
5085 &mut self,
5086 cursor_anchor: Anchor,
5087 new_position: Option<Point>,
5088 cx: &mut ViewContext<Self>,
5089 ) {
5090 if let Some(nav_history) = self.nav_history.as_mut() {
5091 let buffer = self.buffer.read(cx).read(cx);
5092 let cursor_position = cursor_anchor.to_point(&buffer);
5093 let scroll_state = self.scroll_manager.anchor();
5094 let scroll_top_row = scroll_state.top_row(&buffer);
5095 drop(buffer);
5096
5097 if let Some(new_position) = new_position {
5098 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5099 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5100 return;
5101 }
5102 }
5103
5104 nav_history.push(
5105 Some(NavigationData {
5106 cursor_anchor,
5107 cursor_position,
5108 scroll_anchor: scroll_state,
5109 scroll_top_row,
5110 }),
5111 cx,
5112 );
5113 }
5114 }
5115
5116 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5117 let buffer = self.buffer.read(cx).snapshot(cx);
5118 let mut selection = self.selections.first::<usize>(cx);
5119 selection.set_head(buffer.len(), SelectionGoal::None);
5120 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5121 s.select(vec![selection]);
5122 });
5123 }
5124
5125 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5126 let end = self.buffer.read(cx).read(cx).len();
5127 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5128 s.select_ranges(vec![0..end]);
5129 });
5130 }
5131
5132 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5133 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5134 let mut selections = self.selections.all::<Point>(cx);
5135 let max_point = display_map.buffer_snapshot.max_point();
5136 for selection in &mut selections {
5137 let rows = selection.spanned_rows(true, &display_map);
5138 selection.start = Point::new(rows.start, 0);
5139 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5140 selection.reversed = false;
5141 }
5142 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5143 s.select(selections);
5144 });
5145 }
5146
5147 pub fn split_selection_into_lines(
5148 &mut self,
5149 _: &SplitSelectionIntoLines,
5150 cx: &mut ViewContext<Self>,
5151 ) {
5152 let mut to_unfold = Vec::new();
5153 let mut new_selection_ranges = Vec::new();
5154 {
5155 let selections = self.selections.all::<Point>(cx);
5156 let buffer = self.buffer.read(cx).read(cx);
5157 for selection in selections {
5158 for row in selection.start.row..selection.end.row {
5159 let cursor = Point::new(row, buffer.line_len(row));
5160 new_selection_ranges.push(cursor..cursor);
5161 }
5162 new_selection_ranges.push(selection.end..selection.end);
5163 to_unfold.push(selection.start..selection.end);
5164 }
5165 }
5166 self.unfold_ranges(to_unfold, true, true, cx);
5167 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5168 s.select_ranges(new_selection_ranges);
5169 });
5170 }
5171
5172 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5173 self.add_selection(true, cx);
5174 }
5175
5176 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5177 self.add_selection(false, cx);
5178 }
5179
5180 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5181 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5182 let mut selections = self.selections.all::<Point>(cx);
5183 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5184 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5185 let range = oldest_selection.display_range(&display_map).sorted();
5186 let columns = cmp::min(range.start.column(), range.end.column())
5187 ..cmp::max(range.start.column(), range.end.column());
5188
5189 selections.clear();
5190 let mut stack = Vec::new();
5191 for row in range.start.row()..=range.end.row() {
5192 if let Some(selection) = self.selections.build_columnar_selection(
5193 &display_map,
5194 row,
5195 &columns,
5196 oldest_selection.reversed,
5197 ) {
5198 stack.push(selection.id);
5199 selections.push(selection);
5200 }
5201 }
5202
5203 if above {
5204 stack.reverse();
5205 }
5206
5207 AddSelectionsState { above, stack }
5208 });
5209
5210 let last_added_selection = *state.stack.last().unwrap();
5211 let mut new_selections = Vec::new();
5212 if above == state.above {
5213 let end_row = if above {
5214 0
5215 } else {
5216 display_map.max_point().row()
5217 };
5218
5219 'outer: for selection in selections {
5220 if selection.id == last_added_selection {
5221 let range = selection.display_range(&display_map).sorted();
5222 debug_assert_eq!(range.start.row(), range.end.row());
5223 let mut row = range.start.row();
5224 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5225 {
5226 start..end
5227 } else {
5228 cmp::min(range.start.column(), range.end.column())
5229 ..cmp::max(range.start.column(), range.end.column())
5230 };
5231
5232 while row != end_row {
5233 if above {
5234 row -= 1;
5235 } else {
5236 row += 1;
5237 }
5238
5239 if let Some(new_selection) = self.selections.build_columnar_selection(
5240 &display_map,
5241 row,
5242 &columns,
5243 selection.reversed,
5244 ) {
5245 state.stack.push(new_selection.id);
5246 if above {
5247 new_selections.push(new_selection);
5248 new_selections.push(selection);
5249 } else {
5250 new_selections.push(selection);
5251 new_selections.push(new_selection);
5252 }
5253
5254 continue 'outer;
5255 }
5256 }
5257 }
5258
5259 new_selections.push(selection);
5260 }
5261 } else {
5262 new_selections = selections;
5263 new_selections.retain(|s| s.id != last_added_selection);
5264 state.stack.pop();
5265 }
5266
5267 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5268 s.select(new_selections);
5269 });
5270 if state.stack.len() > 1 {
5271 self.add_selections_state = Some(state);
5272 }
5273 }
5274
5275 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5276 self.push_to_selection_history();
5277 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5278 let buffer = &display_map.buffer_snapshot;
5279 let mut selections = self.selections.all::<usize>(cx);
5280 if let Some(mut select_next_state) = self.select_next_state.take() {
5281 let query = &select_next_state.query;
5282 if !select_next_state.done {
5283 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5284 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5285 let mut next_selected_range = None;
5286
5287 let bytes_after_last_selection =
5288 buffer.bytes_in_range(last_selection.end..buffer.len());
5289 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5290 let query_matches = query
5291 .stream_find_iter(bytes_after_last_selection)
5292 .map(|result| (last_selection.end, result))
5293 .chain(
5294 query
5295 .stream_find_iter(bytes_before_first_selection)
5296 .map(|result| (0, result)),
5297 );
5298 for (start_offset, query_match) in query_matches {
5299 let query_match = query_match.unwrap(); // can only fail due to I/O
5300 let offset_range =
5301 start_offset + query_match.start()..start_offset + query_match.end();
5302 let display_range = offset_range.start.to_display_point(&display_map)
5303 ..offset_range.end.to_display_point(&display_map);
5304
5305 if !select_next_state.wordwise
5306 || (!movement::is_inside_word(&display_map, display_range.start)
5307 && !movement::is_inside_word(&display_map, display_range.end))
5308 {
5309 next_selected_range = Some(offset_range);
5310 break;
5311 }
5312 }
5313
5314 if let Some(next_selected_range) = next_selected_range {
5315 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5316 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5317 if action.replace_newest {
5318 s.delete(s.newest_anchor().id);
5319 }
5320 s.insert_range(next_selected_range);
5321 });
5322 } else {
5323 select_next_state.done = true;
5324 }
5325 }
5326
5327 self.select_next_state = Some(select_next_state);
5328 } else if selections.len() == 1 {
5329 let selection = selections.last_mut().unwrap();
5330 if selection.start == selection.end {
5331 let word_range = movement::surrounding_word(
5332 &display_map,
5333 selection.start.to_display_point(&display_map),
5334 );
5335 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5336 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5337 selection.goal = SelectionGoal::None;
5338 selection.reversed = false;
5339
5340 let query = buffer
5341 .text_for_range(selection.start..selection.end)
5342 .collect::<String>();
5343 let select_state = SelectNextState {
5344 query: AhoCorasick::new_auto_configured(&[query]),
5345 wordwise: true,
5346 done: false,
5347 };
5348 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5349 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5350 s.select(selections);
5351 });
5352 self.select_next_state = Some(select_state);
5353 } else {
5354 let query = buffer
5355 .text_for_range(selection.start..selection.end)
5356 .collect::<String>();
5357 self.select_next_state = Some(SelectNextState {
5358 query: AhoCorasick::new_auto_configured(&[query]),
5359 wordwise: false,
5360 done: false,
5361 });
5362 self.select_next(action, cx);
5363 }
5364 }
5365 }
5366
5367 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5368 self.push_to_selection_history();
5369 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5370 let buffer = &display_map.buffer_snapshot;
5371 let mut selections = self.selections.all::<usize>(cx);
5372 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5373 let query = &select_prev_state.query;
5374 if !select_prev_state.done {
5375 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5376 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5377 let mut next_selected_range = None;
5378 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5379 let bytes_before_last_selection =
5380 buffer.reversed_bytes_in_range(0..last_selection.start);
5381 let bytes_after_first_selection =
5382 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5383 let query_matches = query
5384 .stream_find_iter(bytes_before_last_selection)
5385 .map(|result| (last_selection.start, result))
5386 .chain(
5387 query
5388 .stream_find_iter(bytes_after_first_selection)
5389 .map(|result| (buffer.len(), result)),
5390 );
5391 for (end_offset, query_match) in query_matches {
5392 let query_match = query_match.unwrap(); // can only fail due to I/O
5393 let offset_range =
5394 end_offset - query_match.end()..end_offset - query_match.start();
5395 let display_range = offset_range.start.to_display_point(&display_map)
5396 ..offset_range.end.to_display_point(&display_map);
5397
5398 if !select_prev_state.wordwise
5399 || (!movement::is_inside_word(&display_map, display_range.start)
5400 && !movement::is_inside_word(&display_map, display_range.end))
5401 {
5402 next_selected_range = Some(offset_range);
5403 break;
5404 }
5405 }
5406
5407 if let Some(next_selected_range) = next_selected_range {
5408 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5409 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5410 if action.replace_newest {
5411 s.delete(s.newest_anchor().id);
5412 }
5413 s.insert_range(next_selected_range);
5414 });
5415 } else {
5416 select_prev_state.done = true;
5417 }
5418 }
5419
5420 self.select_prev_state = Some(select_prev_state);
5421 } else if selections.len() == 1 {
5422 let selection = selections.last_mut().unwrap();
5423 if selection.start == selection.end {
5424 let word_range = movement::surrounding_word(
5425 &display_map,
5426 selection.start.to_display_point(&display_map),
5427 );
5428 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5429 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5430 selection.goal = SelectionGoal::None;
5431 selection.reversed = false;
5432
5433 let query = buffer
5434 .text_for_range(selection.start..selection.end)
5435 .collect::<String>();
5436 let query = query.chars().rev().collect::<String>();
5437 let select_state = SelectNextState {
5438 query: AhoCorasick::new_auto_configured(&[query]),
5439 wordwise: true,
5440 done: false,
5441 };
5442 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5443 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5444 s.select(selections);
5445 });
5446 self.select_prev_state = Some(select_state);
5447 } else {
5448 let query = buffer
5449 .text_for_range(selection.start..selection.end)
5450 .collect::<String>();
5451 let query = query.chars().rev().collect::<String>();
5452 self.select_prev_state = Some(SelectNextState {
5453 query: AhoCorasick::new_auto_configured(&[query]),
5454 wordwise: false,
5455 done: false,
5456 });
5457 self.select_previous(action, cx);
5458 }
5459 }
5460 }
5461
5462 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5463 self.transact(cx, |this, cx| {
5464 let mut selections = this.selections.all::<Point>(cx);
5465 let mut edits = Vec::new();
5466 let mut selection_edit_ranges = Vec::new();
5467 let mut last_toggled_row = None;
5468 let snapshot = this.buffer.read(cx).read(cx);
5469 let empty_str: Arc<str> = "".into();
5470 let mut suffixes_inserted = Vec::new();
5471
5472 fn comment_prefix_range(
5473 snapshot: &MultiBufferSnapshot,
5474 row: u32,
5475 comment_prefix: &str,
5476 comment_prefix_whitespace: &str,
5477 ) -> Range<Point> {
5478 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5479
5480 let mut line_bytes = snapshot
5481 .bytes_in_range(start..snapshot.max_point())
5482 .flatten()
5483 .copied();
5484
5485 // If this line currently begins with the line comment prefix, then record
5486 // the range containing the prefix.
5487 if line_bytes
5488 .by_ref()
5489 .take(comment_prefix.len())
5490 .eq(comment_prefix.bytes())
5491 {
5492 // Include any whitespace that matches the comment prefix.
5493 let matching_whitespace_len = line_bytes
5494 .zip(comment_prefix_whitespace.bytes())
5495 .take_while(|(a, b)| a == b)
5496 .count() as u32;
5497 let end = Point::new(
5498 start.row,
5499 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5500 );
5501 start..end
5502 } else {
5503 start..start
5504 }
5505 }
5506
5507 fn comment_suffix_range(
5508 snapshot: &MultiBufferSnapshot,
5509 row: u32,
5510 comment_suffix: &str,
5511 comment_suffix_has_leading_space: bool,
5512 ) -> Range<Point> {
5513 let end = Point::new(row, snapshot.line_len(row));
5514 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5515
5516 let mut line_end_bytes = snapshot
5517 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5518 .flatten()
5519 .copied();
5520
5521 let leading_space_len = if suffix_start_column > 0
5522 && line_end_bytes.next() == Some(b' ')
5523 && comment_suffix_has_leading_space
5524 {
5525 1
5526 } else {
5527 0
5528 };
5529
5530 // If this line currently begins with the line comment prefix, then record
5531 // the range containing the prefix.
5532 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5533 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5534 start..end
5535 } else {
5536 end..end
5537 }
5538 }
5539
5540 // TODO: Handle selections that cross excerpts
5541 for selection in &mut selections {
5542 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5543 let language = if let Some(language) =
5544 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5545 {
5546 language
5547 } else {
5548 continue;
5549 };
5550
5551 selection_edit_ranges.clear();
5552
5553 // If multiple selections contain a given row, avoid processing that
5554 // row more than once.
5555 let mut start_row = selection.start.row;
5556 if last_toggled_row == Some(start_row) {
5557 start_row += 1;
5558 }
5559 let end_row =
5560 if selection.end.row > selection.start.row && selection.end.column == 0 {
5561 selection.end.row - 1
5562 } else {
5563 selection.end.row
5564 };
5565 last_toggled_row = Some(end_row);
5566
5567 if start_row > end_row {
5568 continue;
5569 }
5570
5571 // If the language has line comments, toggle those.
5572 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5573 // Split the comment prefix's trailing whitespace into a separate string,
5574 // as that portion won't be used for detecting if a line is a comment.
5575 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5576 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5577 let mut all_selection_lines_are_comments = true;
5578
5579 for row in start_row..=end_row {
5580 if snapshot.is_line_blank(row) && start_row < end_row {
5581 continue;
5582 }
5583
5584 let prefix_range = comment_prefix_range(
5585 snapshot.deref(),
5586 row,
5587 comment_prefix,
5588 comment_prefix_whitespace,
5589 );
5590 if prefix_range.is_empty() {
5591 all_selection_lines_are_comments = false;
5592 }
5593 selection_edit_ranges.push(prefix_range);
5594 }
5595
5596 if all_selection_lines_are_comments {
5597 edits.extend(
5598 selection_edit_ranges
5599 .iter()
5600 .cloned()
5601 .map(|range| (range, empty_str.clone())),
5602 );
5603 } else {
5604 let min_column = selection_edit_ranges
5605 .iter()
5606 .map(|r| r.start.column)
5607 .min()
5608 .unwrap_or(0);
5609 edits.extend(selection_edit_ranges.iter().map(|range| {
5610 let position = Point::new(range.start.row, min_column);
5611 (position..position, full_comment_prefix.clone())
5612 }));
5613 }
5614 } else if let Some((full_comment_prefix, comment_suffix)) =
5615 language.block_comment_delimiters()
5616 {
5617 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5618 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5619 let prefix_range = comment_prefix_range(
5620 snapshot.deref(),
5621 start_row,
5622 comment_prefix,
5623 comment_prefix_whitespace,
5624 );
5625 let suffix_range = comment_suffix_range(
5626 snapshot.deref(),
5627 end_row,
5628 comment_suffix.trim_start_matches(' '),
5629 comment_suffix.starts_with(' '),
5630 );
5631
5632 if prefix_range.is_empty() || suffix_range.is_empty() {
5633 edits.push((
5634 prefix_range.start..prefix_range.start,
5635 full_comment_prefix.clone(),
5636 ));
5637 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5638 suffixes_inserted.push((end_row, comment_suffix.len()));
5639 } else {
5640 edits.push((prefix_range, empty_str.clone()));
5641 edits.push((suffix_range, empty_str.clone()));
5642 }
5643 } else {
5644 continue;
5645 }
5646 }
5647
5648 drop(snapshot);
5649 this.buffer.update(cx, |buffer, cx| {
5650 buffer.edit(edits, None, cx);
5651 });
5652
5653 // Adjust selections so that they end before any comment suffixes that
5654 // were inserted.
5655 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5656 let mut selections = this.selections.all::<Point>(cx);
5657 let snapshot = this.buffer.read(cx).read(cx);
5658 for selection in &mut selections {
5659 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5660 match row.cmp(&selection.end.row) {
5661 Ordering::Less => {
5662 suffixes_inserted.next();
5663 continue;
5664 }
5665 Ordering::Greater => break,
5666 Ordering::Equal => {
5667 if selection.end.column == snapshot.line_len(row) {
5668 if selection.is_empty() {
5669 selection.start.column -= suffix_len as u32;
5670 }
5671 selection.end.column -= suffix_len as u32;
5672 }
5673 break;
5674 }
5675 }
5676 }
5677 }
5678
5679 drop(snapshot);
5680 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5681
5682 let selections = this.selections.all::<Point>(cx);
5683 let selections_on_single_row = selections.windows(2).all(|selections| {
5684 selections[0].start.row == selections[1].start.row
5685 && selections[0].end.row == selections[1].end.row
5686 && selections[0].start.row == selections[0].end.row
5687 });
5688 let selections_selecting = selections
5689 .iter()
5690 .any(|selection| selection.start != selection.end);
5691 let advance_downwards = action.advance_downwards
5692 && selections_on_single_row
5693 && !selections_selecting
5694 && this.mode != EditorMode::SingleLine;
5695
5696 if advance_downwards {
5697 let snapshot = this.buffer.read(cx).snapshot(cx);
5698
5699 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5700 s.move_cursors_with(|display_snapshot, display_point, _| {
5701 let mut point = display_point.to_point(display_snapshot);
5702 point.row += 1;
5703 point = snapshot.clip_point(point, Bias::Left);
5704 let display_point = point.to_display_point(display_snapshot);
5705 (display_point, SelectionGoal::Column(display_point.column()))
5706 })
5707 });
5708 }
5709 });
5710 }
5711
5712 pub fn select_larger_syntax_node(
5713 &mut self,
5714 _: &SelectLargerSyntaxNode,
5715 cx: &mut ViewContext<Self>,
5716 ) {
5717 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5718 let buffer = self.buffer.read(cx).snapshot(cx);
5719 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5720
5721 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5722 let mut selected_larger_node = false;
5723 let new_selections = old_selections
5724 .iter()
5725 .map(|selection| {
5726 let old_range = selection.start..selection.end;
5727 let mut new_range = old_range.clone();
5728 while let Some(containing_range) =
5729 buffer.range_for_syntax_ancestor(new_range.clone())
5730 {
5731 new_range = containing_range;
5732 if !display_map.intersects_fold(new_range.start)
5733 && !display_map.intersects_fold(new_range.end)
5734 {
5735 break;
5736 }
5737 }
5738
5739 selected_larger_node |= new_range != old_range;
5740 Selection {
5741 id: selection.id,
5742 start: new_range.start,
5743 end: new_range.end,
5744 goal: SelectionGoal::None,
5745 reversed: selection.reversed,
5746 }
5747 })
5748 .collect::<Vec<_>>();
5749
5750 if selected_larger_node {
5751 stack.push(old_selections);
5752 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5753 s.select(new_selections);
5754 });
5755 }
5756 self.select_larger_syntax_node_stack = stack;
5757 }
5758
5759 pub fn select_smaller_syntax_node(
5760 &mut self,
5761 _: &SelectSmallerSyntaxNode,
5762 cx: &mut ViewContext<Self>,
5763 ) {
5764 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5765 if let Some(selections) = stack.pop() {
5766 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5767 s.select(selections.to_vec());
5768 });
5769 }
5770 self.select_larger_syntax_node_stack = stack;
5771 }
5772
5773 pub fn move_to_enclosing_bracket(
5774 &mut self,
5775 _: &MoveToEnclosingBracket,
5776 cx: &mut ViewContext<Self>,
5777 ) {
5778 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5779 s.move_offsets_with(|snapshot, selection| {
5780 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5781 return;
5782 };
5783
5784 let mut best_length = usize::MAX;
5785 let mut best_inside = false;
5786 let mut best_in_bracket_range = false;
5787 let mut best_destination = None;
5788 for (open, close) in enclosing_bracket_ranges {
5789 let close = close.to_inclusive();
5790 let length = close.end() - open.start;
5791 let inside = selection.start >= open.end && selection.end <= *close.start();
5792 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5793
5794 // If best is next to a bracket and current isn't, skip
5795 if !in_bracket_range && best_in_bracket_range {
5796 continue;
5797 }
5798
5799 // Prefer smaller lengths unless best is inside and current isn't
5800 if length > best_length && (best_inside || !inside) {
5801 continue;
5802 }
5803
5804 best_length = length;
5805 best_inside = inside;
5806 best_in_bracket_range = in_bracket_range;
5807 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5808 if inside {
5809 open.end
5810 } else {
5811 open.start
5812 }
5813 } else {
5814 if inside {
5815 *close.start()
5816 } else {
5817 *close.end()
5818 }
5819 });
5820 }
5821
5822 if let Some(destination) = best_destination {
5823 selection.collapse_to(destination, SelectionGoal::None);
5824 }
5825 })
5826 });
5827 }
5828
5829 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5830 self.end_selection(cx);
5831 self.selection_history.mode = SelectionHistoryMode::Undoing;
5832 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5833 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5834 self.select_next_state = entry.select_next_state;
5835 self.select_prev_state = entry.select_prev_state;
5836 self.add_selections_state = entry.add_selections_state;
5837 self.request_autoscroll(Autoscroll::newest(), cx);
5838 }
5839 self.selection_history.mode = SelectionHistoryMode::Normal;
5840 }
5841
5842 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5843 self.end_selection(cx);
5844 self.selection_history.mode = SelectionHistoryMode::Redoing;
5845 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5846 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5847 self.select_next_state = entry.select_next_state;
5848 self.select_prev_state = entry.select_prev_state;
5849 self.add_selections_state = entry.add_selections_state;
5850 self.request_autoscroll(Autoscroll::newest(), cx);
5851 }
5852 self.selection_history.mode = SelectionHistoryMode::Normal;
5853 }
5854
5855 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5856 self.go_to_diagnostic_impl(Direction::Next, cx)
5857 }
5858
5859 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5860 self.go_to_diagnostic_impl(Direction::Prev, cx)
5861 }
5862
5863 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5864 let buffer = self.buffer.read(cx).snapshot(cx);
5865 let selection = self.selections.newest::<usize>(cx);
5866
5867 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5868 if direction == Direction::Next {
5869 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5870 let (group_id, jump_to) = popover.activation_info();
5871 if self.activate_diagnostics(group_id, cx) {
5872 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5873 let mut new_selection = s.newest_anchor().clone();
5874 new_selection.collapse_to(jump_to, SelectionGoal::None);
5875 s.select_anchors(vec![new_selection.clone()]);
5876 });
5877 }
5878 return;
5879 }
5880 }
5881
5882 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5883 active_diagnostics
5884 .primary_range
5885 .to_offset(&buffer)
5886 .to_inclusive()
5887 });
5888 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5889 if active_primary_range.contains(&selection.head()) {
5890 *active_primary_range.end()
5891 } else {
5892 selection.head()
5893 }
5894 } else {
5895 selection.head()
5896 };
5897
5898 loop {
5899 let mut diagnostics = if direction == Direction::Prev {
5900 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5901 } else {
5902 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5903 };
5904 let group = diagnostics.find_map(|entry| {
5905 if entry.diagnostic.is_primary
5906 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5907 && !entry.range.is_empty()
5908 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5909 {
5910 Some((entry.range, entry.diagnostic.group_id))
5911 } else {
5912 None
5913 }
5914 });
5915
5916 if let Some((primary_range, group_id)) = group {
5917 if self.activate_diagnostics(group_id, cx) {
5918 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5919 s.select(vec![Selection {
5920 id: selection.id,
5921 start: primary_range.start,
5922 end: primary_range.start,
5923 reversed: false,
5924 goal: SelectionGoal::None,
5925 }]);
5926 });
5927 }
5928 break;
5929 } else {
5930 // Cycle around to the start of the buffer, potentially moving back to the start of
5931 // the currently active diagnostic.
5932 active_primary_range.take();
5933 if direction == Direction::Prev {
5934 if search_start == buffer.len() {
5935 break;
5936 } else {
5937 search_start = buffer.len();
5938 }
5939 } else if search_start == 0 {
5940 break;
5941 } else {
5942 search_start = 0;
5943 }
5944 }
5945 }
5946 }
5947
5948 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5949 let snapshot = self
5950 .display_map
5951 .update(cx, |display_map, cx| display_map.snapshot(cx));
5952 let selection = self.selections.newest::<Point>(cx);
5953
5954 if !self.seek_in_direction(
5955 &snapshot,
5956 selection.head(),
5957 false,
5958 snapshot
5959 .buffer_snapshot
5960 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
5961 cx,
5962 ) {
5963 let wrapped_point = Point::zero();
5964 self.seek_in_direction(
5965 &snapshot,
5966 wrapped_point,
5967 true,
5968 snapshot
5969 .buffer_snapshot
5970 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
5971 cx,
5972 );
5973 }
5974 }
5975
5976 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5977 let snapshot = self
5978 .display_map
5979 .update(cx, |display_map, cx| display_map.snapshot(cx));
5980 let selection = self.selections.newest::<Point>(cx);
5981
5982 if !self.seek_in_direction(
5983 &snapshot,
5984 selection.head(),
5985 false,
5986 snapshot
5987 .buffer_snapshot
5988 .git_diff_hunks_in_range_rev(0..selection.head().row),
5989 cx,
5990 ) {
5991 let wrapped_point = snapshot.buffer_snapshot.max_point();
5992 self.seek_in_direction(
5993 &snapshot,
5994 wrapped_point,
5995 true,
5996 snapshot
5997 .buffer_snapshot
5998 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
5999 cx,
6000 );
6001 }
6002 }
6003
6004 fn seek_in_direction(
6005 &mut self,
6006 snapshot: &DisplaySnapshot,
6007 initial_point: Point,
6008 is_wrapped: bool,
6009 hunks: impl Iterator<Item = DiffHunk<u32>>,
6010 cx: &mut ViewContext<Editor>,
6011 ) -> bool {
6012 let display_point = initial_point.to_display_point(snapshot);
6013 let mut hunks = hunks
6014 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6015 .skip_while(|hunk| {
6016 if is_wrapped {
6017 false
6018 } else {
6019 hunk.contains_display_row(display_point.row())
6020 }
6021 })
6022 .dedup();
6023
6024 if let Some(hunk) = hunks.next() {
6025 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6026 let row = hunk.start_display_row();
6027 let point = DisplayPoint::new(row, 0);
6028 s.select_display_ranges([point..point]);
6029 });
6030
6031 true
6032 } else {
6033 false
6034 }
6035 }
6036
6037 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6038 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
6039 }
6040
6041 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6042 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
6043 }
6044
6045 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
6046 let Some(workspace) = self.workspace(cx) else { return };
6047 let buffer = self.buffer.read(cx);
6048 let head = self.selections.newest::<usize>(cx).head();
6049 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6050 text_anchor
6051 } else {
6052 return;
6053 };
6054
6055 let project = workspace.read(cx).project().clone();
6056 let definitions = project.update(cx, |project, cx| match kind {
6057 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6058 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6059 });
6060
6061 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6062 let definitions = definitions.await?;
6063 editor.update(&mut cx, |editor, cx| {
6064 editor.navigate_to_definitions(definitions, cx);
6065 })?;
6066 Ok::<(), anyhow::Error>(())
6067 })
6068 .detach_and_log_err(cx);
6069 }
6070
6071 pub fn navigate_to_definitions(
6072 &mut self,
6073 mut definitions: Vec<LocationLink>,
6074 cx: &mut ViewContext<Editor>,
6075 ) {
6076 let Some(workspace) = self.workspace(cx) else { return };
6077 let pane = workspace.read(cx).active_pane().clone();
6078 // If there is one definition, just open it directly
6079 if definitions.len() == 1 {
6080 let definition = definitions.pop().unwrap();
6081 let range = definition
6082 .target
6083 .range
6084 .to_offset(definition.target.buffer.read(cx));
6085
6086 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6087 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6088 s.select_ranges([range]);
6089 });
6090 } else {
6091 cx.window_context().defer(move |cx| {
6092 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6093 workspace.open_project_item(definition.target.buffer.clone(), cx)
6094 });
6095 target_editor.update(cx, |target_editor, cx| {
6096 // When selecting a definition in a different buffer, disable the nav history
6097 // to avoid creating a history entry at the previous cursor location.
6098 pane.update(cx, |pane, _| pane.disable_history());
6099 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6100 s.select_ranges([range]);
6101 });
6102 pane.update(cx, |pane, _| pane.enable_history());
6103 });
6104 });
6105 }
6106 } else if !definitions.is_empty() {
6107 let replica_id = self.replica_id(cx);
6108 cx.window_context().defer(move |cx| {
6109 let title = definitions
6110 .iter()
6111 .find(|definition| definition.origin.is_some())
6112 .and_then(|definition| {
6113 definition.origin.as_ref().map(|origin| {
6114 let buffer = origin.buffer.read(cx);
6115 format!(
6116 "Definitions for {}",
6117 buffer
6118 .text_for_range(origin.range.clone())
6119 .collect::<String>()
6120 )
6121 })
6122 })
6123 .unwrap_or("Definitions".to_owned());
6124 let locations = definitions
6125 .into_iter()
6126 .map(|definition| definition.target)
6127 .collect();
6128 workspace.update(cx, |workspace, cx| {
6129 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
6130 });
6131 });
6132 }
6133 }
6134
6135 pub fn find_all_references(
6136 workspace: &mut Workspace,
6137 _: &FindAllReferences,
6138 cx: &mut ViewContext<Workspace>,
6139 ) -> Option<Task<Result<()>>> {
6140 let active_item = workspace.active_item(cx)?;
6141 let editor_handle = active_item.act_as::<Self>(cx)?;
6142
6143 let editor = editor_handle.read(cx);
6144 let buffer = editor.buffer.read(cx);
6145 let head = editor.selections.newest::<usize>(cx).head();
6146 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6147 let replica_id = editor.replica_id(cx);
6148
6149 let project = workspace.project().clone();
6150 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6151 Some(cx.spawn_labeled(
6152 "Finding All References...",
6153 |workspace, mut cx| async move {
6154 let locations = references.await?;
6155 if locations.is_empty() {
6156 return Ok(());
6157 }
6158
6159 workspace.update(&mut cx, |workspace, cx| {
6160 let title = locations
6161 .first()
6162 .as_ref()
6163 .map(|location| {
6164 let buffer = location.buffer.read(cx);
6165 format!(
6166 "References to `{}`",
6167 buffer
6168 .text_for_range(location.range.clone())
6169 .collect::<String>()
6170 )
6171 })
6172 .unwrap();
6173 Self::open_locations_in_multibuffer(
6174 workspace, locations, replica_id, title, cx,
6175 );
6176 })?;
6177
6178 Ok(())
6179 },
6180 ))
6181 }
6182
6183 /// Opens a multibuffer with the given project locations in it
6184 pub fn open_locations_in_multibuffer(
6185 workspace: &mut Workspace,
6186 mut locations: Vec<Location>,
6187 replica_id: ReplicaId,
6188 title: String,
6189 cx: &mut ViewContext<Workspace>,
6190 ) {
6191 // If there are multiple definitions, open them in a multibuffer
6192 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6193 let mut locations = locations.into_iter().peekable();
6194 let mut ranges_to_highlight = Vec::new();
6195
6196 let excerpt_buffer = cx.add_model(|cx| {
6197 let mut multibuffer = MultiBuffer::new(replica_id);
6198 while let Some(location) = locations.next() {
6199 let buffer = location.buffer.read(cx);
6200 let mut ranges_for_buffer = Vec::new();
6201 let range = location.range.to_offset(buffer);
6202 ranges_for_buffer.push(range.clone());
6203
6204 while let Some(next_location) = locations.peek() {
6205 if next_location.buffer == location.buffer {
6206 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6207 locations.next();
6208 } else {
6209 break;
6210 }
6211 }
6212
6213 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6214 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6215 location.buffer.clone(),
6216 ranges_for_buffer,
6217 1,
6218 cx,
6219 ))
6220 }
6221
6222 multibuffer.with_title(title)
6223 });
6224
6225 let editor = cx.add_view(|cx| {
6226 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6227 });
6228 editor.update(cx, |editor, cx| {
6229 editor.highlight_background::<Self>(
6230 ranges_to_highlight,
6231 |theme| theme.editor.highlighted_line_background,
6232 cx,
6233 );
6234 });
6235 workspace.add_item(Box::new(editor), cx);
6236 }
6237
6238 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6239 use language::ToOffset as _;
6240
6241 let project = self.project.clone()?;
6242 let selection = self.selections.newest_anchor().clone();
6243 let (cursor_buffer, cursor_buffer_position) = self
6244 .buffer
6245 .read(cx)
6246 .text_anchor_for_position(selection.head(), cx)?;
6247 let (tail_buffer, _) = self
6248 .buffer
6249 .read(cx)
6250 .text_anchor_for_position(selection.tail(), cx)?;
6251 if tail_buffer != cursor_buffer {
6252 return None;
6253 }
6254
6255 let snapshot = cursor_buffer.read(cx).snapshot();
6256 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6257 let prepare_rename = project.update(cx, |project, cx| {
6258 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6259 });
6260
6261 Some(cx.spawn(|this, mut cx| async move {
6262 let rename_range = if let Some(range) = prepare_rename.await? {
6263 Some(range)
6264 } else {
6265 this.read_with(&cx, |this, cx| {
6266 let buffer = this.buffer.read(cx).snapshot(cx);
6267 let mut buffer_highlights = this
6268 .document_highlights_for_position(selection.head(), &buffer)
6269 .filter(|highlight| {
6270 highlight.start.excerpt_id() == selection.head().excerpt_id()
6271 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6272 });
6273 buffer_highlights
6274 .next()
6275 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6276 })?
6277 };
6278 if let Some(rename_range) = rename_range {
6279 let rename_buffer_range = rename_range.to_offset(&snapshot);
6280 let cursor_offset_in_rename_range =
6281 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6282
6283 this.update(&mut cx, |this, cx| {
6284 this.take_rename(false, cx);
6285 let style = this.style(cx);
6286 let buffer = this.buffer.read(cx).read(cx);
6287 let cursor_offset = selection.head().to_offset(&buffer);
6288 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6289 let rename_end = rename_start + rename_buffer_range.len();
6290 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6291 let mut old_highlight_id = None;
6292 let old_name: Arc<str> = buffer
6293 .chunks(rename_start..rename_end, true)
6294 .map(|chunk| {
6295 if old_highlight_id.is_none() {
6296 old_highlight_id = chunk.syntax_highlight_id;
6297 }
6298 chunk.text
6299 })
6300 .collect::<String>()
6301 .into();
6302
6303 drop(buffer);
6304
6305 // Position the selection in the rename editor so that it matches the current selection.
6306 this.show_local_selections = false;
6307 let rename_editor = cx.add_view(|cx| {
6308 let mut editor = Editor::single_line(None, cx);
6309 if let Some(old_highlight_id) = old_highlight_id {
6310 editor.override_text_style =
6311 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6312 }
6313 editor.buffer.update(cx, |buffer, cx| {
6314 buffer.edit([(0..0, old_name.clone())], None, cx)
6315 });
6316 editor.select_all(&SelectAll, cx);
6317 editor
6318 });
6319
6320 let ranges = this
6321 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6322 .into_iter()
6323 .flat_map(|(_, ranges)| ranges)
6324 .chain(
6325 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6326 .into_iter()
6327 .flat_map(|(_, ranges)| ranges),
6328 )
6329 .collect();
6330
6331 this.highlight_text::<Rename>(
6332 ranges,
6333 HighlightStyle {
6334 fade_out: Some(style.rename_fade),
6335 ..Default::default()
6336 },
6337 cx,
6338 );
6339 cx.focus(&rename_editor);
6340 let block_id = this.insert_blocks(
6341 [BlockProperties {
6342 style: BlockStyle::Flex,
6343 position: range.start.clone(),
6344 height: 1,
6345 render: Arc::new({
6346 let editor = rename_editor.clone();
6347 move |cx: &mut BlockContext| {
6348 ChildView::new(&editor, cx)
6349 .contained()
6350 .with_padding_left(cx.anchor_x)
6351 .into_any()
6352 }
6353 }),
6354 disposition: BlockDisposition::Below,
6355 }],
6356 Some(Autoscroll::fit()),
6357 cx,
6358 )[0];
6359 this.pending_rename = Some(RenameState {
6360 range,
6361 old_name,
6362 editor: rename_editor,
6363 block_id,
6364 });
6365 })?;
6366 }
6367
6368 Ok(())
6369 }))
6370 }
6371
6372 pub fn confirm_rename(
6373 workspace: &mut Workspace,
6374 _: &ConfirmRename,
6375 cx: &mut ViewContext<Workspace>,
6376 ) -> Option<Task<Result<()>>> {
6377 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6378
6379 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6380 let rename = editor.take_rename(false, cx)?;
6381 let buffer = editor.buffer.read(cx);
6382 let (start_buffer, start) =
6383 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6384 let (end_buffer, end) =
6385 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6386 if start_buffer == end_buffer {
6387 let new_name = rename.editor.read(cx).text(cx);
6388 Some((start_buffer, start..end, rename.old_name, new_name))
6389 } else {
6390 None
6391 }
6392 })?;
6393
6394 let rename = workspace.project().clone().update(cx, |project, cx| {
6395 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6396 });
6397
6398 let editor = editor.downgrade();
6399 Some(cx.spawn(|workspace, mut cx| async move {
6400 let project_transaction = rename.await?;
6401 Self::open_project_transaction(
6402 &editor,
6403 workspace,
6404 project_transaction,
6405 format!("Rename: {} → {}", old_name, new_name),
6406 cx.clone(),
6407 )
6408 .await?;
6409
6410 editor.update(&mut cx, |editor, cx| {
6411 editor.refresh_document_highlights(cx);
6412 })?;
6413 Ok(())
6414 }))
6415 }
6416
6417 fn take_rename(
6418 &mut self,
6419 moving_cursor: bool,
6420 cx: &mut ViewContext<Self>,
6421 ) -> Option<RenameState> {
6422 let rename = self.pending_rename.take()?;
6423 self.remove_blocks(
6424 [rename.block_id].into_iter().collect(),
6425 Some(Autoscroll::fit()),
6426 cx,
6427 );
6428 self.clear_text_highlights::<Rename>(cx);
6429 self.show_local_selections = true;
6430
6431 if moving_cursor {
6432 let rename_editor = rename.editor.read(cx);
6433 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6434
6435 // Update the selection to match the position of the selection inside
6436 // the rename editor.
6437 let snapshot = self.buffer.read(cx).read(cx);
6438 let rename_range = rename.range.to_offset(&snapshot);
6439 let cursor_in_editor = snapshot
6440 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6441 .min(rename_range.end);
6442 drop(snapshot);
6443
6444 self.change_selections(None, cx, |s| {
6445 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6446 });
6447 } else {
6448 self.refresh_document_highlights(cx);
6449 }
6450
6451 Some(rename)
6452 }
6453
6454 #[cfg(any(test, feature = "test-support"))]
6455 pub fn pending_rename(&self) -> Option<&RenameState> {
6456 self.pending_rename.as_ref()
6457 }
6458
6459 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6460 let project = match &self.project {
6461 Some(project) => project.clone(),
6462 None => return None,
6463 };
6464
6465 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6466 }
6467
6468 fn perform_format(
6469 &mut self,
6470 project: ModelHandle<Project>,
6471 trigger: FormatTrigger,
6472 cx: &mut ViewContext<Self>,
6473 ) -> Task<Result<()>> {
6474 let buffer = self.buffer().clone();
6475 let buffers = buffer.read(cx).all_buffers();
6476
6477 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6478 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6479
6480 cx.spawn(|_, mut cx| async move {
6481 let transaction = futures::select_biased! {
6482 _ = timeout => {
6483 log::warn!("timed out waiting for formatting");
6484 None
6485 }
6486 transaction = format.log_err().fuse() => transaction,
6487 };
6488
6489 buffer.update(&mut cx, |buffer, cx| {
6490 if let Some(transaction) = transaction {
6491 if !buffer.is_singleton() {
6492 buffer.push_transaction(&transaction.0, cx);
6493 }
6494 }
6495
6496 cx.notify();
6497 });
6498
6499 Ok(())
6500 })
6501 }
6502
6503 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6504 if let Some(project) = self.project.clone() {
6505 self.buffer.update(cx, |multi_buffer, cx| {
6506 project.update(cx, |project, cx| {
6507 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6508 });
6509 })
6510 }
6511 }
6512
6513 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6514 cx.show_character_palette();
6515 }
6516
6517 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6518 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6519 let buffer = self.buffer.read(cx).snapshot(cx);
6520 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6521 let is_valid = buffer
6522 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6523 .any(|entry| {
6524 entry.diagnostic.is_primary
6525 && !entry.range.is_empty()
6526 && entry.range.start == primary_range_start
6527 && entry.diagnostic.message == active_diagnostics.primary_message
6528 });
6529
6530 if is_valid != active_diagnostics.is_valid {
6531 active_diagnostics.is_valid = is_valid;
6532 let mut new_styles = HashMap::default();
6533 for (block_id, diagnostic) in &active_diagnostics.blocks {
6534 new_styles.insert(
6535 *block_id,
6536 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6537 );
6538 }
6539 self.display_map
6540 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6541 }
6542 }
6543 }
6544
6545 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6546 self.dismiss_diagnostics(cx);
6547 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6548 let buffer = self.buffer.read(cx).snapshot(cx);
6549
6550 let mut primary_range = None;
6551 let mut primary_message = None;
6552 let mut group_end = Point::zero();
6553 let diagnostic_group = buffer
6554 .diagnostic_group::<Point>(group_id)
6555 .map(|entry| {
6556 if entry.range.end > group_end {
6557 group_end = entry.range.end;
6558 }
6559 if entry.diagnostic.is_primary {
6560 primary_range = Some(entry.range.clone());
6561 primary_message = Some(entry.diagnostic.message.clone());
6562 }
6563 entry
6564 })
6565 .collect::<Vec<_>>();
6566 let primary_range = primary_range?;
6567 let primary_message = primary_message?;
6568 let primary_range =
6569 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6570
6571 let blocks = display_map
6572 .insert_blocks(
6573 diagnostic_group.iter().map(|entry| {
6574 let diagnostic = entry.diagnostic.clone();
6575 let message_height = diagnostic.message.lines().count() as u8;
6576 BlockProperties {
6577 style: BlockStyle::Fixed,
6578 position: buffer.anchor_after(entry.range.start),
6579 height: message_height,
6580 render: diagnostic_block_renderer(diagnostic, true),
6581 disposition: BlockDisposition::Below,
6582 }
6583 }),
6584 cx,
6585 )
6586 .into_iter()
6587 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6588 .collect();
6589
6590 Some(ActiveDiagnosticGroup {
6591 primary_range,
6592 primary_message,
6593 blocks,
6594 is_valid: true,
6595 })
6596 });
6597 self.active_diagnostics.is_some()
6598 }
6599
6600 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6601 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6602 self.display_map.update(cx, |display_map, cx| {
6603 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6604 });
6605 cx.notify();
6606 }
6607 }
6608
6609 pub fn set_selections_from_remote(
6610 &mut self,
6611 selections: Vec<Selection<Anchor>>,
6612 pending_selection: Option<Selection<Anchor>>,
6613 cx: &mut ViewContext<Self>,
6614 ) {
6615 let old_cursor_position = self.selections.newest_anchor().head();
6616 self.selections.change_with(cx, |s| {
6617 s.select_anchors(selections);
6618 if let Some(pending_selection) = pending_selection {
6619 s.set_pending(pending_selection, SelectMode::Character);
6620 } else {
6621 s.clear_pending();
6622 }
6623 });
6624 self.selections_did_change(false, &old_cursor_position, cx);
6625 }
6626
6627 fn push_to_selection_history(&mut self) {
6628 self.selection_history.push(SelectionHistoryEntry {
6629 selections: self.selections.disjoint_anchors(),
6630 select_next_state: self.select_next_state.clone(),
6631 select_prev_state: self.select_prev_state.clone(),
6632 add_selections_state: self.add_selections_state.clone(),
6633 });
6634 }
6635
6636 pub fn transact(
6637 &mut self,
6638 cx: &mut ViewContext<Self>,
6639 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6640 ) -> Option<TransactionId> {
6641 self.start_transaction_at(Instant::now(), cx);
6642 update(self, cx);
6643 self.end_transaction_at(Instant::now(), cx)
6644 }
6645
6646 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6647 self.end_selection(cx);
6648 if let Some(tx_id) = self
6649 .buffer
6650 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6651 {
6652 self.selection_history
6653 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6654 }
6655 }
6656
6657 fn end_transaction_at(
6658 &mut self,
6659 now: Instant,
6660 cx: &mut ViewContext<Self>,
6661 ) -> Option<TransactionId> {
6662 if let Some(tx_id) = self
6663 .buffer
6664 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6665 {
6666 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6667 *end_selections = Some(self.selections.disjoint_anchors());
6668 } else {
6669 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6670 }
6671
6672 cx.emit(Event::Edited);
6673 Some(tx_id)
6674 } else {
6675 None
6676 }
6677 }
6678
6679 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6680 let mut fold_ranges = Vec::new();
6681
6682 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6683
6684 let selections = self.selections.all::<Point>(cx);
6685 for selection in selections {
6686 let range = selection.range().sorted();
6687 let buffer_start_row = range.start.row;
6688
6689 for row in (0..=range.end.row).rev() {
6690 let fold_range = display_map.foldable_range(row);
6691
6692 if let Some(fold_range) = fold_range {
6693 if fold_range.end.row >= buffer_start_row {
6694 fold_ranges.push(fold_range);
6695 if row <= range.start.row {
6696 break;
6697 }
6698 }
6699 }
6700 }
6701 }
6702
6703 self.fold_ranges(fold_ranges, true, cx);
6704 }
6705
6706 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6707 let buffer_row = fold_at.buffer_row;
6708 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6709
6710 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6711 let autoscroll = self
6712 .selections
6713 .all::<Point>(cx)
6714 .iter()
6715 .any(|selection| fold_range.overlaps(&selection.range()));
6716
6717 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6718 }
6719 }
6720
6721 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6722 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6723 let buffer = &display_map.buffer_snapshot;
6724 let selections = self.selections.all::<Point>(cx);
6725 let ranges = selections
6726 .iter()
6727 .map(|s| {
6728 let range = s.display_range(&display_map).sorted();
6729 let mut start = range.start.to_point(&display_map);
6730 let mut end = range.end.to_point(&display_map);
6731 start.column = 0;
6732 end.column = buffer.line_len(end.row);
6733 start..end
6734 })
6735 .collect::<Vec<_>>();
6736
6737 self.unfold_ranges(ranges, true, true, cx);
6738 }
6739
6740 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6741 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6742
6743 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6744 ..Point::new(
6745 unfold_at.buffer_row,
6746 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6747 );
6748
6749 let autoscroll = self
6750 .selections
6751 .all::<Point>(cx)
6752 .iter()
6753 .any(|selection| selection.range().overlaps(&intersection_range));
6754
6755 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6756 }
6757
6758 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6759 let selections = self.selections.all::<Point>(cx);
6760 let ranges = selections.into_iter().map(|s| s.start..s.end);
6761 self.fold_ranges(ranges, true, cx);
6762 }
6763
6764 pub fn fold_ranges<T: ToOffset + Clone>(
6765 &mut self,
6766 ranges: impl IntoIterator<Item = Range<T>>,
6767 auto_scroll: bool,
6768 cx: &mut ViewContext<Self>,
6769 ) {
6770 let mut ranges = ranges.into_iter().peekable();
6771 if ranges.peek().is_some() {
6772 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6773
6774 if auto_scroll {
6775 self.request_autoscroll(Autoscroll::fit(), cx);
6776 }
6777
6778 cx.notify();
6779 }
6780 }
6781
6782 pub fn unfold_ranges<T: ToOffset + Clone>(
6783 &mut self,
6784 ranges: impl IntoIterator<Item = Range<T>>,
6785 inclusive: bool,
6786 auto_scroll: bool,
6787 cx: &mut ViewContext<Self>,
6788 ) {
6789 let mut ranges = ranges.into_iter().peekable();
6790 if ranges.peek().is_some() {
6791 self.display_map
6792 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6793 if auto_scroll {
6794 self.request_autoscroll(Autoscroll::fit(), cx);
6795 }
6796
6797 cx.notify();
6798 }
6799 }
6800
6801 pub fn gutter_hover(
6802 &mut self,
6803 GutterHover { hovered }: &GutterHover,
6804 cx: &mut ViewContext<Self>,
6805 ) {
6806 self.gutter_hovered = *hovered;
6807 cx.notify();
6808 }
6809
6810 pub fn insert_blocks(
6811 &mut self,
6812 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6813 autoscroll: Option<Autoscroll>,
6814 cx: &mut ViewContext<Self>,
6815 ) -> Vec<BlockId> {
6816 let blocks = self
6817 .display_map
6818 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6819 if let Some(autoscroll) = autoscroll {
6820 self.request_autoscroll(autoscroll, cx);
6821 }
6822 blocks
6823 }
6824
6825 pub fn replace_blocks(
6826 &mut self,
6827 blocks: HashMap<BlockId, RenderBlock>,
6828 autoscroll: Option<Autoscroll>,
6829 cx: &mut ViewContext<Self>,
6830 ) {
6831 self.display_map
6832 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6833 if let Some(autoscroll) = autoscroll {
6834 self.request_autoscroll(autoscroll, cx);
6835 }
6836 }
6837
6838 pub fn remove_blocks(
6839 &mut self,
6840 block_ids: HashSet<BlockId>,
6841 autoscroll: Option<Autoscroll>,
6842 cx: &mut ViewContext<Self>,
6843 ) {
6844 self.display_map.update(cx, |display_map, cx| {
6845 display_map.remove_blocks(block_ids, cx)
6846 });
6847 if let Some(autoscroll) = autoscroll {
6848 self.request_autoscroll(autoscroll, cx);
6849 }
6850 }
6851
6852 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6853 self.display_map
6854 .update(cx, |map, cx| map.snapshot(cx))
6855 .longest_row()
6856 }
6857
6858 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6859 self.display_map
6860 .update(cx, |map, cx| map.snapshot(cx))
6861 .max_point()
6862 }
6863
6864 pub fn text(&self, cx: &AppContext) -> String {
6865 self.buffer.read(cx).read(cx).text()
6866 }
6867
6868 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6869 self.transact(cx, |this, cx| {
6870 this.buffer
6871 .read(cx)
6872 .as_singleton()
6873 .expect("you can only call set_text on editors for singleton buffers")
6874 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6875 });
6876 }
6877
6878 pub fn display_text(&self, cx: &mut AppContext) -> String {
6879 self.display_map
6880 .update(cx, |map, cx| map.snapshot(cx))
6881 .text()
6882 }
6883
6884 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6885 let settings = self.buffer.read(cx).settings_at(0, cx);
6886 let mode = self
6887 .soft_wrap_mode_override
6888 .unwrap_or_else(|| settings.soft_wrap);
6889 match mode {
6890 language_settings::SoftWrap::None => SoftWrap::None,
6891 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6892 language_settings::SoftWrap::PreferredLineLength => {
6893 SoftWrap::Column(settings.preferred_line_length)
6894 }
6895 }
6896 }
6897
6898 pub fn set_soft_wrap_mode(
6899 &mut self,
6900 mode: language_settings::SoftWrap,
6901 cx: &mut ViewContext<Self>,
6902 ) {
6903 self.soft_wrap_mode_override = Some(mode);
6904 cx.notify();
6905 }
6906
6907 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6908 self.display_map
6909 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6910 }
6911
6912 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6913 if self.soft_wrap_mode_override.is_some() {
6914 self.soft_wrap_mode_override.take();
6915 } else {
6916 let soft_wrap = match self.soft_wrap_mode(cx) {
6917 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
6918 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
6919 };
6920 self.soft_wrap_mode_override = Some(soft_wrap);
6921 }
6922 cx.notify();
6923 }
6924
6925 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
6926 self.show_gutter = show_gutter;
6927 cx.notify();
6928 }
6929
6930 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6931 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6932 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6933 cx.reveal_path(&file.abs_path(cx));
6934 }
6935 }
6936 }
6937
6938 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6939 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6940 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6941 if let Some(path) = file.abs_path(cx).to_str() {
6942 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6943 }
6944 }
6945 }
6946 }
6947
6948 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6949 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6950 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6951 if let Some(path) = file.path().to_str() {
6952 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6953 }
6954 }
6955 }
6956 }
6957
6958 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6959 self.highlighted_rows = rows;
6960 }
6961
6962 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6963 self.highlighted_rows.clone()
6964 }
6965
6966 pub fn highlight_background<T: 'static>(
6967 &mut self,
6968 ranges: Vec<Range<Anchor>>,
6969 color_fetcher: fn(&Theme) -> Color,
6970 cx: &mut ViewContext<Self>,
6971 ) {
6972 self.background_highlights
6973 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6974 cx.notify();
6975 }
6976
6977 #[allow(clippy::type_complexity)]
6978 pub fn clear_background_highlights<T: 'static>(
6979 &mut self,
6980 cx: &mut ViewContext<Self>,
6981 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6982 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6983 if highlights.is_some() {
6984 cx.notify();
6985 }
6986 highlights
6987 }
6988
6989 #[cfg(feature = "test-support")]
6990 pub fn all_background_highlights(
6991 &mut self,
6992 cx: &mut ViewContext<Self>,
6993 ) -> Vec<(Range<DisplayPoint>, Color)> {
6994 let snapshot = self.snapshot(cx);
6995 let buffer = &snapshot.buffer_snapshot;
6996 let start = buffer.anchor_before(0);
6997 let end = buffer.anchor_after(buffer.len());
6998 let theme = theme::current(cx);
6999 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7000 }
7001
7002 fn document_highlights_for_position<'a>(
7003 &'a self,
7004 position: Anchor,
7005 buffer: &'a MultiBufferSnapshot,
7006 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7007 let read_highlights = self
7008 .background_highlights
7009 .get(&TypeId::of::<DocumentHighlightRead>())
7010 .map(|h| &h.1);
7011 let write_highlights = self
7012 .background_highlights
7013 .get(&TypeId::of::<DocumentHighlightWrite>())
7014 .map(|h| &h.1);
7015 let left_position = position.bias_left(buffer);
7016 let right_position = position.bias_right(buffer);
7017 read_highlights
7018 .into_iter()
7019 .chain(write_highlights)
7020 .flat_map(move |ranges| {
7021 let start_ix = match ranges.binary_search_by(|probe| {
7022 let cmp = probe.end.cmp(&left_position, buffer);
7023 if cmp.is_ge() {
7024 Ordering::Greater
7025 } else {
7026 Ordering::Less
7027 }
7028 }) {
7029 Ok(i) | Err(i) => i,
7030 };
7031
7032 let right_position = right_position.clone();
7033 ranges[start_ix..]
7034 .iter()
7035 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7036 })
7037 }
7038
7039 pub fn background_highlights_in_range(
7040 &self,
7041 search_range: Range<Anchor>,
7042 display_snapshot: &DisplaySnapshot,
7043 theme: &Theme,
7044 ) -> Vec<(Range<DisplayPoint>, Color)> {
7045 let mut results = Vec::new();
7046 let buffer = &display_snapshot.buffer_snapshot;
7047 for (color_fetcher, ranges) in self.background_highlights.values() {
7048 let color = color_fetcher(theme);
7049 let start_ix = match ranges.binary_search_by(|probe| {
7050 let cmp = probe.end.cmp(&search_range.start, buffer);
7051 if cmp.is_gt() {
7052 Ordering::Greater
7053 } else {
7054 Ordering::Less
7055 }
7056 }) {
7057 Ok(i) | Err(i) => i,
7058 };
7059 for range in &ranges[start_ix..] {
7060 if range.start.cmp(&search_range.end, buffer).is_ge() {
7061 break;
7062 }
7063 let start = range
7064 .start
7065 .to_point(buffer)
7066 .to_display_point(display_snapshot);
7067 let end = range
7068 .end
7069 .to_point(buffer)
7070 .to_display_point(display_snapshot);
7071 results.push((start..end, color))
7072 }
7073 }
7074 results
7075 }
7076
7077 pub fn highlight_text<T: 'static>(
7078 &mut self,
7079 ranges: Vec<Range<Anchor>>,
7080 style: HighlightStyle,
7081 cx: &mut ViewContext<Self>,
7082 ) {
7083 self.display_map.update(cx, |map, _| {
7084 map.highlight_text(TypeId::of::<T>(), ranges, style)
7085 });
7086 cx.notify();
7087 }
7088
7089 pub fn text_highlights<'a, T: 'static>(
7090 &'a self,
7091 cx: &'a AppContext,
7092 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7093 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7094 }
7095
7096 pub fn clear_text_highlights<T: 'static>(
7097 &mut self,
7098 cx: &mut ViewContext<Self>,
7099 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7100 let highlights = self
7101 .display_map
7102 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7103 if highlights.is_some() {
7104 cx.notify();
7105 }
7106 highlights
7107 }
7108
7109 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7110 self.blink_manager.read(cx).visible() && self.focused
7111 }
7112
7113 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7114 cx.notify();
7115 }
7116
7117 fn on_buffer_event(
7118 &mut self,
7119 _: ModelHandle<MultiBuffer>,
7120 event: &multi_buffer::Event,
7121 cx: &mut ViewContext<Self>,
7122 ) {
7123 match event {
7124 multi_buffer::Event::Edited => {
7125 self.refresh_active_diagnostics(cx);
7126 self.refresh_code_actions(cx);
7127 if self.has_active_copilot_suggestion(cx) {
7128 self.update_visible_copilot_suggestion(cx);
7129 }
7130 cx.emit(Event::BufferEdited);
7131 }
7132 multi_buffer::Event::ExcerptsAdded {
7133 buffer,
7134 predecessor,
7135 excerpts,
7136 } => cx.emit(Event::ExcerptsAdded {
7137 buffer: buffer.clone(),
7138 predecessor: *predecessor,
7139 excerpts: excerpts.clone(),
7140 }),
7141 multi_buffer::Event::ExcerptsRemoved { ids } => {
7142 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7143 }
7144 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7145 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7146 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7147 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7148 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7149 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7150 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7151 multi_buffer::Event::DiagnosticsUpdated => {
7152 self.refresh_active_diagnostics(cx);
7153 }
7154 _ => {}
7155 }
7156 }
7157
7158 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7159 cx.notify();
7160 }
7161
7162 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7163 self.refresh_copilot_suggestions(true, cx);
7164 }
7165
7166 pub fn set_searchable(&mut self, searchable: bool) {
7167 self.searchable = searchable;
7168 }
7169
7170 pub fn searchable(&self) -> bool {
7171 self.searchable
7172 }
7173
7174 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7175 let active_item = workspace.active_item(cx);
7176 let editor_handle = if let Some(editor) = active_item
7177 .as_ref()
7178 .and_then(|item| item.act_as::<Self>(cx))
7179 {
7180 editor
7181 } else {
7182 cx.propagate_action();
7183 return;
7184 };
7185
7186 let editor = editor_handle.read(cx);
7187 let buffer = editor.buffer.read(cx);
7188 if buffer.is_singleton() {
7189 cx.propagate_action();
7190 return;
7191 }
7192
7193 let mut new_selections_by_buffer = HashMap::default();
7194 for selection in editor.selections.all::<usize>(cx) {
7195 for (buffer, mut range, _) in
7196 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7197 {
7198 if selection.reversed {
7199 mem::swap(&mut range.start, &mut range.end);
7200 }
7201 new_selections_by_buffer
7202 .entry(buffer)
7203 .or_insert(Vec::new())
7204 .push(range)
7205 }
7206 }
7207
7208 editor_handle.update(cx, |editor, cx| {
7209 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7210 });
7211 let pane = workspace.active_pane().clone();
7212 pane.update(cx, |pane, _| pane.disable_history());
7213
7214 // We defer the pane interaction because we ourselves are a workspace item
7215 // and activating a new item causes the pane to call a method on us reentrantly,
7216 // which panics if we're on the stack.
7217 cx.defer(move |workspace, cx| {
7218 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7219 let editor = workspace.open_project_item::<Self>(buffer, cx);
7220 editor.update(cx, |editor, cx| {
7221 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7222 s.select_ranges(ranges);
7223 });
7224 });
7225 }
7226
7227 pane.update(cx, |pane, _| pane.enable_history());
7228 });
7229 }
7230
7231 fn jump(
7232 workspace: &mut Workspace,
7233 path: ProjectPath,
7234 position: Point,
7235 anchor: language::Anchor,
7236 cx: &mut ViewContext<Workspace>,
7237 ) {
7238 let editor = workspace.open_path(path, None, true, cx);
7239 cx.spawn(|_, mut cx| async move {
7240 let editor = editor
7241 .await?
7242 .downcast::<Editor>()
7243 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7244 .downgrade();
7245 editor.update(&mut cx, |editor, cx| {
7246 let buffer = editor
7247 .buffer()
7248 .read(cx)
7249 .as_singleton()
7250 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7251 let buffer = buffer.read(cx);
7252 let cursor = if buffer.can_resolve(&anchor) {
7253 language::ToPoint::to_point(&anchor, buffer)
7254 } else {
7255 buffer.clip_point(position, Bias::Left)
7256 };
7257
7258 let nav_history = editor.nav_history.take();
7259 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7260 s.select_ranges([cursor..cursor]);
7261 });
7262 editor.nav_history = nav_history;
7263
7264 anyhow::Ok(())
7265 })??;
7266
7267 anyhow::Ok(())
7268 })
7269 .detach_and_log_err(cx);
7270 }
7271
7272 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7273 let snapshot = self.buffer.read(cx).read(cx);
7274 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7275 Some(
7276 ranges
7277 .iter()
7278 .map(move |range| {
7279 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7280 })
7281 .collect(),
7282 )
7283 }
7284
7285 fn selection_replacement_ranges(
7286 &self,
7287 range: Range<OffsetUtf16>,
7288 cx: &AppContext,
7289 ) -> Vec<Range<OffsetUtf16>> {
7290 let selections = self.selections.all::<OffsetUtf16>(cx);
7291 let newest_selection = selections
7292 .iter()
7293 .max_by_key(|selection| selection.id)
7294 .unwrap();
7295 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7296 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7297 let snapshot = self.buffer.read(cx).read(cx);
7298 selections
7299 .into_iter()
7300 .map(|mut selection| {
7301 selection.start.0 =
7302 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7303 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7304 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7305 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7306 })
7307 .collect()
7308 }
7309
7310 fn report_copilot_event(
7311 &self,
7312 suggestion_id: Option<String>,
7313 suggestion_accepted: bool,
7314 cx: &AppContext,
7315 ) {
7316 let Some(project) = &self.project else {
7317 return
7318 };
7319
7320 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7321 let file_extension = self
7322 .buffer
7323 .read(cx)
7324 .as_singleton()
7325 .and_then(|b| b.read(cx).file())
7326 .and_then(|file| Path::new(file.file_name(cx)).extension())
7327 .and_then(|e| e.to_str())
7328 .map(|a| a.to_string());
7329
7330 let telemetry = project.read(cx).client().telemetry().clone();
7331 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7332
7333 let event = ClickhouseEvent::Copilot {
7334 suggestion_id,
7335 suggestion_accepted,
7336 file_extension,
7337 };
7338 telemetry.report_clickhouse_event(event, telemetry_settings);
7339 }
7340
7341 fn report_editor_event(
7342 &self,
7343 name: &'static str,
7344 file_extension: Option<String>,
7345 cx: &AppContext,
7346 ) {
7347 let Some(project) = &self.project else {
7348 return
7349 };
7350
7351 // If None, we are in a file without an extension
7352 let file = self
7353 .buffer
7354 .read(cx)
7355 .as_singleton()
7356 .and_then(|b| b.read(cx).file());
7357 let file_extension = file_extension.or(file
7358 .as_ref()
7359 .and_then(|file| Path::new(file.file_name(cx)).extension())
7360 .and_then(|e| e.to_str())
7361 .map(|a| a.to_string()));
7362
7363 let vim_mode = cx
7364 .global::<SettingsStore>()
7365 .raw_user_settings()
7366 .get("vim_mode")
7367 == Some(&serde_json::Value::Bool(true));
7368 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7369 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7370 let copilot_enabled_for_language = self
7371 .buffer
7372 .read(cx)
7373 .settings_at(0, cx)
7374 .show_copilot_suggestions;
7375
7376 let telemetry = project.read(cx).client().telemetry().clone();
7377 let event = ClickhouseEvent::Editor {
7378 file_extension,
7379 vim_mode,
7380 operation: name,
7381 copilot_enabled,
7382 copilot_enabled_for_language,
7383 };
7384 telemetry.report_clickhouse_event(event, telemetry_settings)
7385 }
7386
7387 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7388 /// with each line being an array of {text, highlight} objects.
7389 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7390 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7391 return;
7392 };
7393
7394 #[derive(Serialize)]
7395 struct Chunk<'a> {
7396 text: String,
7397 highlight: Option<&'a str>,
7398 }
7399
7400 let snapshot = buffer.read(cx).snapshot();
7401 let range = self
7402 .selected_text_range(cx)
7403 .and_then(|selected_range| {
7404 if selected_range.is_empty() {
7405 None
7406 } else {
7407 Some(selected_range)
7408 }
7409 })
7410 .unwrap_or_else(|| 0..snapshot.len());
7411
7412 let chunks = snapshot.chunks(range, true);
7413 let mut lines = Vec::new();
7414 let mut line: VecDeque<Chunk> = VecDeque::new();
7415
7416 let theme = &theme::current(cx).editor.syntax;
7417
7418 for chunk in chunks {
7419 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7420 let mut chunk_lines = chunk.text.split("\n").peekable();
7421 while let Some(text) = chunk_lines.next() {
7422 let mut merged_with_last_token = false;
7423 if let Some(last_token) = line.back_mut() {
7424 if last_token.highlight == highlight {
7425 last_token.text.push_str(text);
7426 merged_with_last_token = true;
7427 }
7428 }
7429
7430 if !merged_with_last_token {
7431 line.push_back(Chunk {
7432 text: text.into(),
7433 highlight,
7434 });
7435 }
7436
7437 if chunk_lines.peek().is_some() {
7438 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7439 line.pop_front();
7440 }
7441 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7442 line.pop_back();
7443 }
7444
7445 lines.push(mem::take(&mut line));
7446 }
7447 }
7448 }
7449
7450 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7451 cx.write_to_clipboard(ClipboardItem::new(lines));
7452 }
7453}
7454
7455fn consume_contiguous_rows(
7456 contiguous_row_selections: &mut Vec<Selection<Point>>,
7457 selection: &Selection<Point>,
7458 display_map: &DisplaySnapshot,
7459 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7460) -> (u32, u32) {
7461 contiguous_row_selections.push(selection.clone());
7462 let start_row = selection.start.row;
7463 let mut end_row = ending_row(selection, display_map);
7464
7465 while let Some(next_selection) = selections.peek() {
7466 if next_selection.start.row <= end_row {
7467 end_row = ending_row(next_selection, display_map);
7468 contiguous_row_selections.push(selections.next().unwrap().clone());
7469 } else {
7470 break;
7471 }
7472 }
7473 (start_row, end_row)
7474}
7475
7476fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7477 if next_selection.end.column > 0 || next_selection.is_empty() {
7478 display_map.next_line_boundary(next_selection.end).0.row + 1
7479 } else {
7480 next_selection.end.row
7481 }
7482}
7483
7484impl EditorSnapshot {
7485 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7486 self.display_snapshot.buffer_snapshot.language_at(position)
7487 }
7488
7489 pub fn is_focused(&self) -> bool {
7490 self.is_focused
7491 }
7492
7493 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7494 self.placeholder_text.as_ref()
7495 }
7496
7497 pub fn scroll_position(&self) -> Vector2F {
7498 self.scroll_anchor.scroll_position(&self.display_snapshot)
7499 }
7500}
7501
7502impl Deref for EditorSnapshot {
7503 type Target = DisplaySnapshot;
7504
7505 fn deref(&self) -> &Self::Target {
7506 &self.display_snapshot
7507 }
7508}
7509
7510#[derive(Clone, Debug, PartialEq, Eq)]
7511pub enum Event {
7512 InputIgnored {
7513 text: Arc<str>,
7514 },
7515 ExcerptsAdded {
7516 buffer: ModelHandle<Buffer>,
7517 predecessor: ExcerptId,
7518 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7519 },
7520 ExcerptsRemoved {
7521 ids: Vec<ExcerptId>,
7522 },
7523 BufferEdited,
7524 Edited,
7525 Reparsed,
7526 Focused,
7527 Blurred,
7528 DirtyChanged,
7529 Saved,
7530 TitleChanged,
7531 DiffBaseChanged,
7532 SelectionsChanged {
7533 local: bool,
7534 },
7535 ScrollPositionChanged {
7536 local: bool,
7537 autoscroll: bool,
7538 },
7539 Closed,
7540}
7541
7542pub struct EditorFocused(pub ViewHandle<Editor>);
7543pub struct EditorBlurred(pub ViewHandle<Editor>);
7544pub struct EditorReleased(pub WeakViewHandle<Editor>);
7545
7546impl Entity for Editor {
7547 type Event = Event;
7548
7549 fn release(&mut self, cx: &mut AppContext) {
7550 cx.emit_global(EditorReleased(self.handle.clone()));
7551 }
7552}
7553
7554impl View for Editor {
7555 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7556 let style = self.style(cx);
7557 let font_changed = self.display_map.update(cx, |map, cx| {
7558 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7559 map.set_font(style.text.font_id, style.text.font_size, cx)
7560 });
7561
7562 if font_changed {
7563 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7564 hide_hover(editor, cx);
7565 hide_link_definition(editor, cx);
7566 });
7567 }
7568
7569 Stack::new()
7570 .with_child(EditorElement::new(style.clone()))
7571 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7572 .into_any()
7573 }
7574
7575 fn ui_name() -> &'static str {
7576 "Editor"
7577 }
7578
7579 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7580 if cx.is_self_focused() {
7581 let focused_event = EditorFocused(cx.handle());
7582 cx.emit(Event::Focused);
7583 cx.emit_global(focused_event);
7584 }
7585 if let Some(rename) = self.pending_rename.as_ref() {
7586 cx.focus(&rename.editor);
7587 } else {
7588 if !self.focused {
7589 self.blink_manager.update(cx, BlinkManager::enable);
7590 }
7591 self.focused = true;
7592 self.buffer.update(cx, |buffer, cx| {
7593 buffer.finalize_last_transaction(cx);
7594 if self.leader_replica_id.is_none() {
7595 buffer.set_active_selections(
7596 &self.selections.disjoint_anchors(),
7597 self.selections.line_mode,
7598 self.cursor_shape,
7599 cx,
7600 );
7601 }
7602 });
7603 }
7604 }
7605
7606 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7607 let blurred_event = EditorBlurred(cx.handle());
7608 cx.emit_global(blurred_event);
7609 self.focused = false;
7610 self.blink_manager.update(cx, BlinkManager::disable);
7611 self.buffer
7612 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7613 self.hide_context_menu(cx);
7614 hide_hover(self, cx);
7615 cx.emit(Event::Blurred);
7616 cx.notify();
7617 }
7618
7619 fn modifiers_changed(
7620 &mut self,
7621 event: &gpui::platform::ModifiersChangedEvent,
7622 cx: &mut ViewContext<Self>,
7623 ) -> bool {
7624 let pending_selection = self.has_pending_selection();
7625
7626 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7627 if event.cmd && !pending_selection {
7628 let snapshot = self.snapshot(cx);
7629 let kind = if event.shift {
7630 LinkDefinitionKind::Type
7631 } else {
7632 LinkDefinitionKind::Symbol
7633 };
7634
7635 show_link_definition(kind, self, point, snapshot, cx);
7636 return false;
7637 }
7638 }
7639
7640 {
7641 if self.link_go_to_definition_state.symbol_range.is_some()
7642 || !self.link_go_to_definition_state.definitions.is_empty()
7643 {
7644 self.link_go_to_definition_state.symbol_range.take();
7645 self.link_go_to_definition_state.definitions.clear();
7646 cx.notify();
7647 }
7648
7649 self.link_go_to_definition_state.task = None;
7650
7651 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7652 }
7653
7654 false
7655 }
7656
7657 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7658 Self::reset_to_default_keymap_context(keymap);
7659 let mode = match self.mode {
7660 EditorMode::SingleLine => "single_line",
7661 EditorMode::AutoHeight { .. } => "auto_height",
7662 EditorMode::Full => "full",
7663 };
7664 keymap.add_key("mode", mode);
7665 if self.pending_rename.is_some() {
7666 keymap.add_identifier("renaming");
7667 }
7668 match self.context_menu.as_ref() {
7669 Some(ContextMenu::Completions(_)) => {
7670 keymap.add_identifier("menu");
7671 keymap.add_identifier("showing_completions")
7672 }
7673 Some(ContextMenu::CodeActions(_)) => {
7674 keymap.add_identifier("menu");
7675 keymap.add_identifier("showing_code_actions")
7676 }
7677 None => {}
7678 }
7679 for layer in self.keymap_context_layers.values() {
7680 keymap.extend(layer);
7681 }
7682
7683 if let Some(extension) = self
7684 .buffer
7685 .read(cx)
7686 .as_singleton()
7687 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7688 {
7689 keymap.add_key("extension", extension.to_string());
7690 }
7691 }
7692
7693 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7694 Some(
7695 self.buffer
7696 .read(cx)
7697 .read(cx)
7698 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7699 .collect(),
7700 )
7701 }
7702
7703 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7704 // Prevent the IME menu from appearing when holding down an alphabetic key
7705 // while input is disabled.
7706 if !self.input_enabled {
7707 return None;
7708 }
7709
7710 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7711 Some(range.start.0..range.end.0)
7712 }
7713
7714 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7715 let snapshot = self.buffer.read(cx).read(cx);
7716 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7717 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7718 }
7719
7720 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7721 self.clear_text_highlights::<InputComposition>(cx);
7722 self.ime_transaction.take();
7723 }
7724
7725 fn replace_text_in_range(
7726 &mut self,
7727 range_utf16: Option<Range<usize>>,
7728 text: &str,
7729 cx: &mut ViewContext<Self>,
7730 ) {
7731 self.transact(cx, |this, cx| {
7732 if this.input_enabled {
7733 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7734 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7735 Some(this.selection_replacement_ranges(range_utf16, cx))
7736 } else {
7737 this.marked_text_ranges(cx)
7738 };
7739
7740 if let Some(new_selected_ranges) = new_selected_ranges {
7741 this.change_selections(None, cx, |selections| {
7742 selections.select_ranges(new_selected_ranges)
7743 });
7744 }
7745 }
7746
7747 this.handle_input(text, cx);
7748 });
7749
7750 if !self.input_enabled {
7751 return;
7752 }
7753
7754 if let Some(transaction) = self.ime_transaction {
7755 self.buffer.update(cx, |buffer, cx| {
7756 buffer.group_until_transaction(transaction, cx);
7757 });
7758 }
7759
7760 self.unmark_text(cx);
7761 }
7762
7763 fn replace_and_mark_text_in_range(
7764 &mut self,
7765 range_utf16: Option<Range<usize>>,
7766 text: &str,
7767 new_selected_range_utf16: Option<Range<usize>>,
7768 cx: &mut ViewContext<Self>,
7769 ) {
7770 if !self.input_enabled {
7771 return;
7772 }
7773
7774 let transaction = self.transact(cx, |this, cx| {
7775 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7776 let snapshot = this.buffer.read(cx).read(cx);
7777 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7778 for marked_range in &mut marked_ranges {
7779 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7780 marked_range.start.0 += relative_range_utf16.start;
7781 marked_range.start =
7782 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7783 marked_range.end =
7784 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7785 }
7786 }
7787 Some(marked_ranges)
7788 } else if let Some(range_utf16) = range_utf16 {
7789 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7790 Some(this.selection_replacement_ranges(range_utf16, cx))
7791 } else {
7792 None
7793 };
7794
7795 if let Some(ranges) = ranges_to_replace {
7796 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7797 }
7798
7799 let marked_ranges = {
7800 let snapshot = this.buffer.read(cx).read(cx);
7801 this.selections
7802 .disjoint_anchors()
7803 .iter()
7804 .map(|selection| {
7805 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7806 })
7807 .collect::<Vec<_>>()
7808 };
7809
7810 if text.is_empty() {
7811 this.unmark_text(cx);
7812 } else {
7813 this.highlight_text::<InputComposition>(
7814 marked_ranges.clone(),
7815 this.style(cx).composition_mark,
7816 cx,
7817 );
7818 }
7819
7820 this.handle_input(text, cx);
7821
7822 if let Some(new_selected_range) = new_selected_range_utf16 {
7823 let snapshot = this.buffer.read(cx).read(cx);
7824 let new_selected_ranges = marked_ranges
7825 .into_iter()
7826 .map(|marked_range| {
7827 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7828 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7829 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7830 snapshot.clip_offset_utf16(new_start, Bias::Left)
7831 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7832 })
7833 .collect::<Vec<_>>();
7834
7835 drop(snapshot);
7836 this.change_selections(None, cx, |selections| {
7837 selections.select_ranges(new_selected_ranges)
7838 });
7839 }
7840 });
7841
7842 self.ime_transaction = self.ime_transaction.or(transaction);
7843 if let Some(transaction) = self.ime_transaction {
7844 self.buffer.update(cx, |buffer, cx| {
7845 buffer.group_until_transaction(transaction, cx);
7846 });
7847 }
7848
7849 if self.text_highlights::<InputComposition>(cx).is_none() {
7850 self.ime_transaction.take();
7851 }
7852 }
7853}
7854
7855fn build_style(
7856 settings: &ThemeSettings,
7857 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7858 override_text_style: Option<&OverrideTextStyle>,
7859 cx: &AppContext,
7860) -> EditorStyle {
7861 let font_cache = cx.font_cache();
7862
7863 let theme_id = settings.theme.meta.id;
7864 let mut theme = settings.theme.editor.clone();
7865 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7866 let field_editor_theme = get_field_editor_theme(&settings.theme);
7867 theme.text_color = field_editor_theme.text.color;
7868 theme.selection = field_editor_theme.selection;
7869 theme.background = field_editor_theme
7870 .container
7871 .background_color
7872 .unwrap_or_default();
7873 EditorStyle {
7874 text: field_editor_theme.text,
7875 placeholder_text: field_editor_theme.placeholder_text,
7876 theme,
7877 theme_id,
7878 }
7879 } else {
7880 let font_family_id = settings.buffer_font_family;
7881 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7882 let font_properties = Default::default();
7883 let font_id = font_cache
7884 .select_font(font_family_id, &font_properties)
7885 .unwrap();
7886 let font_size = settings.buffer_font_size(cx);
7887 EditorStyle {
7888 text: TextStyle {
7889 color: settings.theme.editor.text_color,
7890 font_family_name,
7891 font_family_id,
7892 font_id,
7893 font_size,
7894 font_properties,
7895 underline: Default::default(),
7896 },
7897 placeholder_text: None,
7898 theme,
7899 theme_id,
7900 }
7901 };
7902
7903 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7904 if let Some(highlighted) = style
7905 .text
7906 .clone()
7907 .highlight(highlight_style, font_cache)
7908 .log_err()
7909 {
7910 style.text = highlighted;
7911 }
7912 }
7913
7914 style
7915}
7916
7917trait SelectionExt {
7918 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7919 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7920 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7921 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7922 -> Range<u32>;
7923}
7924
7925impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7926 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7927 let start = self.start.to_point(buffer);
7928 let end = self.end.to_point(buffer);
7929 if self.reversed {
7930 end..start
7931 } else {
7932 start..end
7933 }
7934 }
7935
7936 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7937 let start = self.start.to_offset(buffer);
7938 let end = self.end.to_offset(buffer);
7939 if self.reversed {
7940 end..start
7941 } else {
7942 start..end
7943 }
7944 }
7945
7946 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7947 let start = self
7948 .start
7949 .to_point(&map.buffer_snapshot)
7950 .to_display_point(map);
7951 let end = self
7952 .end
7953 .to_point(&map.buffer_snapshot)
7954 .to_display_point(map);
7955 if self.reversed {
7956 end..start
7957 } else {
7958 start..end
7959 }
7960 }
7961
7962 fn spanned_rows(
7963 &self,
7964 include_end_if_at_line_start: bool,
7965 map: &DisplaySnapshot,
7966 ) -> Range<u32> {
7967 let start = self.start.to_point(&map.buffer_snapshot);
7968 let mut end = self.end.to_point(&map.buffer_snapshot);
7969 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7970 end.row -= 1;
7971 }
7972
7973 let buffer_start = map.prev_line_boundary(start).0;
7974 let buffer_end = map.next_line_boundary(end).0;
7975 buffer_start.row..buffer_end.row + 1
7976 }
7977}
7978
7979impl<T: InvalidationRegion> InvalidationStack<T> {
7980 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7981 where
7982 S: Clone + ToOffset,
7983 {
7984 while let Some(region) = self.last() {
7985 let all_selections_inside_invalidation_ranges =
7986 if selections.len() == region.ranges().len() {
7987 selections
7988 .iter()
7989 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7990 .all(|(selection, invalidation_range)| {
7991 let head = selection.head().to_offset(buffer);
7992 invalidation_range.start <= head && invalidation_range.end >= head
7993 })
7994 } else {
7995 false
7996 };
7997
7998 if all_selections_inside_invalidation_ranges {
7999 break;
8000 } else {
8001 self.pop();
8002 }
8003 }
8004 }
8005}
8006
8007impl<T> Default for InvalidationStack<T> {
8008 fn default() -> Self {
8009 Self(Default::default())
8010 }
8011}
8012
8013impl<T> Deref for InvalidationStack<T> {
8014 type Target = Vec<T>;
8015
8016 fn deref(&self) -> &Self::Target {
8017 &self.0
8018 }
8019}
8020
8021impl<T> DerefMut for InvalidationStack<T> {
8022 fn deref_mut(&mut self) -> &mut Self::Target {
8023 &mut self.0
8024 }
8025}
8026
8027impl InvalidationRegion for SnippetState {
8028 fn ranges(&self) -> &[Range<Anchor>] {
8029 &self.ranges[self.active_index]
8030 }
8031}
8032
8033impl Deref for EditorStyle {
8034 type Target = theme::Editor;
8035
8036 fn deref(&self) -> &Self::Target {
8037 &self.theme
8038 }
8039}
8040
8041pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8042 let mut highlighted_lines = Vec::new();
8043
8044 for (index, line) in diagnostic.message.lines().enumerate() {
8045 let line = match &diagnostic.source {
8046 Some(source) if index == 0 => {
8047 let source_highlight = Vec::from_iter(0..source.len());
8048 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8049 }
8050
8051 _ => highlight_diagnostic_message(Vec::new(), line),
8052 };
8053 highlighted_lines.push(line);
8054 }
8055 let message = diagnostic.message;
8056 Arc::new(move |cx: &mut BlockContext| {
8057 let message = message.clone();
8058 let settings = settings::get::<ThemeSettings>(cx);
8059 let tooltip_style = settings.theme.tooltip.clone();
8060 let theme = &settings.theme.editor;
8061 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8062 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8063 let anchor_x = cx.anchor_x;
8064 enum BlockContextToolip {}
8065 MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
8066 Flex::column()
8067 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8068 Label::new(
8069 line.clone(),
8070 style.message.clone().with_font_size(font_size),
8071 )
8072 .with_highlights(highlights.clone())
8073 .contained()
8074 .with_margin_left(anchor_x)
8075 }))
8076 .aligned()
8077 .left()
8078 .into_any()
8079 })
8080 .with_cursor_style(CursorStyle::PointingHand)
8081 .on_click(MouseButton::Left, move |_, _, cx| {
8082 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8083 })
8084 // We really need to rethink this ID system...
8085 .with_tooltip::<BlockContextToolip>(
8086 cx.block_id,
8087 "Copy diagnostic message".to_string(),
8088 None,
8089 tooltip_style,
8090 cx,
8091 )
8092 .into_any()
8093 })
8094}
8095
8096pub fn highlight_diagnostic_message(
8097 initial_highlights: Vec<usize>,
8098 message: &str,
8099) -> (String, Vec<usize>) {
8100 let mut message_without_backticks = String::new();
8101 let mut prev_offset = 0;
8102 let mut inside_block = false;
8103 let mut highlights = initial_highlights;
8104 for (match_ix, (offset, _)) in message
8105 .match_indices('`')
8106 .chain([(message.len(), "")])
8107 .enumerate()
8108 {
8109 message_without_backticks.push_str(&message[prev_offset..offset]);
8110 if inside_block {
8111 highlights.extend(prev_offset - match_ix..offset - match_ix);
8112 }
8113
8114 inside_block = !inside_block;
8115 prev_offset = offset + 1;
8116 }
8117
8118 (message_without_backticks, highlights)
8119}
8120
8121pub fn diagnostic_style(
8122 severity: DiagnosticSeverity,
8123 valid: bool,
8124 theme: &theme::Editor,
8125) -> DiagnosticStyle {
8126 match (severity, valid) {
8127 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8128 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8129 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8130 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8131 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8132 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8133 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8134 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8135 _ => theme.invalid_hint_diagnostic.clone(),
8136 }
8137}
8138
8139pub fn combine_syntax_and_fuzzy_match_highlights(
8140 text: &str,
8141 default_style: HighlightStyle,
8142 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8143 match_indices: &[usize],
8144) -> Vec<(Range<usize>, HighlightStyle)> {
8145 let mut result = Vec::new();
8146 let mut match_indices = match_indices.iter().copied().peekable();
8147
8148 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8149 {
8150 syntax_highlight.weight = None;
8151
8152 // Add highlights for any fuzzy match characters before the next
8153 // syntax highlight range.
8154 while let Some(&match_index) = match_indices.peek() {
8155 if match_index >= range.start {
8156 break;
8157 }
8158 match_indices.next();
8159 let end_index = char_ix_after(match_index, text);
8160 let mut match_style = default_style;
8161 match_style.weight = Some(fonts::Weight::BOLD);
8162 result.push((match_index..end_index, match_style));
8163 }
8164
8165 if range.start == usize::MAX {
8166 break;
8167 }
8168
8169 // Add highlights for any fuzzy match characters within the
8170 // syntax highlight range.
8171 let mut offset = range.start;
8172 while let Some(&match_index) = match_indices.peek() {
8173 if match_index >= range.end {
8174 break;
8175 }
8176
8177 match_indices.next();
8178 if match_index > offset {
8179 result.push((offset..match_index, syntax_highlight));
8180 }
8181
8182 let mut end_index = char_ix_after(match_index, text);
8183 while let Some(&next_match_index) = match_indices.peek() {
8184 if next_match_index == end_index && next_match_index < range.end {
8185 end_index = char_ix_after(next_match_index, text);
8186 match_indices.next();
8187 } else {
8188 break;
8189 }
8190 }
8191
8192 let mut match_style = syntax_highlight;
8193 match_style.weight = Some(fonts::Weight::BOLD);
8194 result.push((match_index..end_index, match_style));
8195 offset = end_index;
8196 }
8197
8198 if offset < range.end {
8199 result.push((offset..range.end, syntax_highlight));
8200 }
8201 }
8202
8203 fn char_ix_after(ix: usize, text: &str) -> usize {
8204 ix + text[ix..].chars().next().unwrap().len_utf8()
8205 }
8206
8207 result
8208}
8209
8210pub fn styled_runs_for_code_label<'a>(
8211 label: &'a CodeLabel,
8212 syntax_theme: &'a theme::SyntaxTheme,
8213) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8214 let fade_out = HighlightStyle {
8215 fade_out: Some(0.35),
8216 ..Default::default()
8217 };
8218
8219 let mut prev_end = label.filter_range.end;
8220 label
8221 .runs
8222 .iter()
8223 .enumerate()
8224 .flat_map(move |(ix, (range, highlight_id))| {
8225 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8226 style
8227 } else {
8228 return Default::default();
8229 };
8230 let mut muted_style = style;
8231 muted_style.highlight(fade_out);
8232
8233 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8234 if range.start >= label.filter_range.end {
8235 if range.start > prev_end {
8236 runs.push((prev_end..range.start, fade_out));
8237 }
8238 runs.push((range.clone(), muted_style));
8239 } else if range.end <= label.filter_range.end {
8240 runs.push((range.clone(), style));
8241 } else {
8242 runs.push((range.start..label.filter_range.end, style));
8243 runs.push((label.filter_range.end..range.end, muted_style));
8244 }
8245 prev_end = cmp::max(prev_end, range.end);
8246
8247 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8248 runs.push((prev_end..label.text.len(), fade_out));
8249 }
8250
8251 runs
8252 })
8253}
8254
8255pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8256 let mut index = 0;
8257 let mut codepoints = text.char_indices().peekable();
8258
8259 std::iter::from_fn(move || {
8260 let start_index = index;
8261 while let Some((new_index, codepoint)) = codepoints.next() {
8262 index = new_index + codepoint.len_utf8();
8263 let current_upper = codepoint.is_uppercase();
8264 let next_upper = codepoints
8265 .peek()
8266 .map(|(_, c)| c.is_uppercase())
8267 .unwrap_or(false);
8268
8269 if !current_upper && next_upper {
8270 return Some(&text[start_index..index]);
8271 }
8272 }
8273
8274 index = text.len();
8275 if start_index < text.len() {
8276 return Some(&text[start_index..]);
8277 }
8278 None
8279 })
8280 .flat_map(|word| word.split_inclusive('_'))
8281}
8282
8283trait RangeToAnchorExt {
8284 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8285}
8286
8287impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8288 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8289 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8290 }
8291}