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