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