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