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