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