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