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