1mod blink_manager;
2pub mod display_map;
3mod element;
4
5mod git;
6mod highlight_matching_bracket;
7mod hover_popover;
8pub mod items;
9mod link_go_to_definition;
10mod mouse_context_menu;
11pub mod movement;
12mod multi_buffer;
13mod persistence;
14pub mod scroll;
15pub mod selections_collection;
16
17#[cfg(test)]
18mod editor_tests;
19#[cfg(any(test, feature = "test-support"))]
20pub mod test;
21
22use aho_corasick::AhoCorasick;
23use anyhow::Result;
24use blink_manager::BlinkManager;
25use clock::ReplicaId;
26use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
27use copilot::Copilot;
28pub use display_map::DisplayPoint;
29use display_map::*;
30pub use element::*;
31use futures::FutureExt;
32use fuzzy::{StringMatch, StringMatchCandidate};
33use gpui::{
34 actions,
35 color::Color,
36 elements::*,
37 executor,
38 fonts::{self, HighlightStyle, TextStyle},
39 geometry::vector::Vector2F,
40 impl_actions, impl_internal_actions,
41 keymap_matcher::KeymapContext,
42 platform::CursorStyle,
43 serde_json::{self, json},
44 AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
45 ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View,
46 ViewContext, ViewHandle, WeakViewHandle,
47};
48use highlight_matching_bracket::refresh_matching_bracket_highlights;
49use hover_popover::{hide_hover, HideHover, HoverState};
50pub use items::MAX_TAB_TITLE_LEN;
51use itertools::Itertools;
52pub use language::{char_kind, CharKind};
53use language::{
54 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
55 Diagnostic, DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16,
56 Point, Selection, SelectionGoal, TransactionId,
57};
58use link_go_to_definition::{
59 hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
60};
61pub use multi_buffer::{
62 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
63 ToPoint,
64};
65use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
66use ordered_float::OrderedFloat;
67use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
68use scroll::{
69 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
70};
71use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
72use serde::{Deserialize, Serialize};
73use settings::Settings;
74use smallvec::SmallVec;
75use snippet::Snippet;
76use std::{
77 any::TypeId,
78 borrow::Cow,
79 cmp::{self, Ordering, Reverse},
80 mem,
81 num::NonZeroU32,
82 ops::{Deref, DerefMut, Range},
83 path::Path,
84 sync::Arc,
85 time::{Duration, Instant},
86};
87pub use sum_tree::Bias;
88use theme::{DiagnosticStyle, Theme};
89use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
90use workspace::{ItemNavHistory, ViewId, Workspace, WorkspaceId};
91
92use crate::git::diff_hunk_to_display;
93
94const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
95const MAX_LINE_LEN: usize = 1024;
96const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
97const MAX_SELECTION_HISTORY_LEN: usize = 1024;
98
99pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
100
101#[derive(Clone, Deserialize, PartialEq, Default)]
102pub struct SelectNext {
103 #[serde(default)]
104 pub replace_newest: bool,
105}
106
107#[derive(Clone, PartialEq)]
108pub struct Select(pub SelectPhase);
109
110#[derive(Clone, Debug, PartialEq)]
111pub struct Jump {
112 path: ProjectPath,
113 position: Point,
114 anchor: language::Anchor,
115}
116
117#[derive(Clone, Deserialize, PartialEq)]
118pub struct SelectToBeginningOfLine {
119 #[serde(default)]
120 stop_at_soft_wraps: bool,
121}
122
123#[derive(Clone, Default, Deserialize, PartialEq)]
124pub struct MovePageUp {
125 #[serde(default)]
126 center_cursor: bool,
127}
128
129#[derive(Clone, Default, Deserialize, PartialEq)]
130pub struct MovePageDown {
131 #[serde(default)]
132 center_cursor: bool,
133}
134
135#[derive(Clone, Deserialize, PartialEq)]
136pub struct SelectToEndOfLine {
137 #[serde(default)]
138 stop_at_soft_wraps: bool,
139}
140
141#[derive(Clone, Deserialize, PartialEq)]
142pub struct ToggleCodeActions {
143 #[serde(default)]
144 pub deployed_from_indicator: bool,
145}
146
147#[derive(Clone, Default, Deserialize, PartialEq)]
148pub struct ConfirmCompletion {
149 #[serde(default)]
150 pub item_ix: Option<usize>,
151}
152
153#[derive(Clone, Default, Deserialize, PartialEq)]
154pub struct ConfirmCodeAction {
155 #[serde(default)]
156 pub item_ix: Option<usize>,
157}
158
159#[derive(Clone, Default, Deserialize, PartialEq)]
160pub struct ToggleComments {
161 #[serde(default)]
162 pub advance_downwards: bool,
163}
164
165#[derive(Clone, Default, Deserialize, PartialEq)]
166pub struct FoldAt {
167 pub buffer_row: u32,
168}
169
170#[derive(Clone, Default, Deserialize, PartialEq)]
171pub struct UnfoldAt {
172 pub buffer_row: u32,
173}
174
175#[derive(Clone, Default, Deserialize, PartialEq)]
176pub struct GutterHover {
177 pub hovered: bool,
178}
179
180actions!(
181 editor,
182 [
183 Cancel,
184 Backspace,
185 Delete,
186 Newline,
187 NewlineBelow,
188 GoToDiagnostic,
189 GoToPrevDiagnostic,
190 GoToHunk,
191 GoToPrevHunk,
192 Indent,
193 Outdent,
194 DeleteLine,
195 DeleteToPreviousWordStart,
196 DeleteToPreviousSubwordStart,
197 DeleteToNextWordEnd,
198 DeleteToNextSubwordEnd,
199 DeleteToBeginningOfLine,
200 DeleteToEndOfLine,
201 CutToEndOfLine,
202 DuplicateLine,
203 MoveLineUp,
204 MoveLineDown,
205 Transpose,
206 Cut,
207 Copy,
208 Paste,
209 Undo,
210 Redo,
211 MoveUp,
212 PageUp,
213 MoveDown,
214 PageDown,
215 MoveLeft,
216 MoveRight,
217 MoveToPreviousWordStart,
218 MoveToPreviousSubwordStart,
219 MoveToNextWordEnd,
220 MoveToNextSubwordEnd,
221 MoveToBeginningOfLine,
222 MoveToEndOfLine,
223 MoveToBeginning,
224 MoveToEnd,
225 SelectUp,
226 SelectDown,
227 SelectLeft,
228 SelectRight,
229 SelectToPreviousWordStart,
230 SelectToPreviousSubwordStart,
231 SelectToNextWordEnd,
232 SelectToNextSubwordEnd,
233 SelectToBeginning,
234 SelectToEnd,
235 SelectAll,
236 SelectLine,
237 SplitSelectionIntoLines,
238 AddSelectionAbove,
239 AddSelectionBelow,
240 Tab,
241 TabPrev,
242 ShowCharacterPalette,
243 SelectLargerSyntaxNode,
244 SelectSmallerSyntaxNode,
245 GoToDefinition,
246 GoToTypeDefinition,
247 MoveToEnclosingBracket,
248 UndoSelection,
249 RedoSelection,
250 FindAllReferences,
251 Rename,
252 ConfirmRename,
253 Fold,
254 UnfoldLines,
255 FoldSelectedRanges,
256 ShowCompletions,
257 OpenExcerpts,
258 RestartLanguageServer,
259 Hover,
260 Format,
261 ToggleSoftWrap,
262 RevealInFinder,
263 CopyHighlightJson
264 ]
265);
266
267impl_actions!(
268 editor,
269 [
270 SelectNext,
271 SelectToBeginningOfLine,
272 SelectToEndOfLine,
273 ToggleCodeActions,
274 MovePageUp,
275 MovePageDown,
276 ConfirmCompletion,
277 ConfirmCodeAction,
278 ToggleComments,
279 FoldAt,
280 UnfoldAt,
281 GutterHover
282 ]
283);
284
285impl_internal_actions!(editor, [Select, Jump]);
286
287enum DocumentHighlightRead {}
288enum DocumentHighlightWrite {}
289enum InputComposition {}
290
291#[derive(Copy, Clone, PartialEq, Eq)]
292pub enum Direction {
293 Prev,
294 Next,
295}
296
297pub fn init(cx: &mut MutableAppContext) {
298 cx.add_action(Editor::new_file);
299 cx.add_action(Editor::select);
300 cx.add_action(Editor::cancel);
301 cx.add_action(Editor::newline);
302 cx.add_action(Editor::newline_below);
303 cx.add_action(Editor::backspace);
304 cx.add_action(Editor::delete);
305 cx.add_action(Editor::tab);
306 cx.add_action(Editor::tab_prev);
307 cx.add_action(Editor::indent);
308 cx.add_action(Editor::outdent);
309 cx.add_action(Editor::delete_line);
310 cx.add_action(Editor::delete_to_previous_word_start);
311 cx.add_action(Editor::delete_to_previous_subword_start);
312 cx.add_action(Editor::delete_to_next_word_end);
313 cx.add_action(Editor::delete_to_next_subword_end);
314 cx.add_action(Editor::delete_to_beginning_of_line);
315 cx.add_action(Editor::delete_to_end_of_line);
316 cx.add_action(Editor::cut_to_end_of_line);
317 cx.add_action(Editor::duplicate_line);
318 cx.add_action(Editor::move_line_up);
319 cx.add_action(Editor::move_line_down);
320 cx.add_action(Editor::transpose);
321 cx.add_action(Editor::cut);
322 cx.add_action(Editor::copy);
323 cx.add_action(Editor::paste);
324 cx.add_action(Editor::undo);
325 cx.add_action(Editor::redo);
326 cx.add_action(Editor::move_up);
327 cx.add_action(Editor::move_page_up);
328 cx.add_action(Editor::move_down);
329 cx.add_action(Editor::move_page_down);
330 cx.add_action(Editor::next_screen);
331 cx.add_action(Editor::move_left);
332 cx.add_action(Editor::move_right);
333 cx.add_action(Editor::move_to_previous_word_start);
334 cx.add_action(Editor::move_to_previous_subword_start);
335 cx.add_action(Editor::move_to_next_word_end);
336 cx.add_action(Editor::move_to_next_subword_end);
337 cx.add_action(Editor::move_to_beginning_of_line);
338 cx.add_action(Editor::move_to_end_of_line);
339 cx.add_action(Editor::move_to_beginning);
340 cx.add_action(Editor::move_to_end);
341 cx.add_action(Editor::select_up);
342 cx.add_action(Editor::select_down);
343 cx.add_action(Editor::select_left);
344 cx.add_action(Editor::select_right);
345 cx.add_action(Editor::select_to_previous_word_start);
346 cx.add_action(Editor::select_to_previous_subword_start);
347 cx.add_action(Editor::select_to_next_word_end);
348 cx.add_action(Editor::select_to_next_subword_end);
349 cx.add_action(Editor::select_to_beginning_of_line);
350 cx.add_action(Editor::select_to_end_of_line);
351 cx.add_action(Editor::select_to_beginning);
352 cx.add_action(Editor::select_to_end);
353 cx.add_action(Editor::select_all);
354 cx.add_action(Editor::select_line);
355 cx.add_action(Editor::split_selection_into_lines);
356 cx.add_action(Editor::add_selection_above);
357 cx.add_action(Editor::add_selection_below);
358 cx.add_action(Editor::select_next);
359 cx.add_action(Editor::toggle_comments);
360 cx.add_action(Editor::select_larger_syntax_node);
361 cx.add_action(Editor::select_smaller_syntax_node);
362 cx.add_action(Editor::move_to_enclosing_bracket);
363 cx.add_action(Editor::undo_selection);
364 cx.add_action(Editor::redo_selection);
365 cx.add_action(Editor::go_to_diagnostic);
366 cx.add_action(Editor::go_to_prev_diagnostic);
367 cx.add_action(Editor::go_to_hunk);
368 cx.add_action(Editor::go_to_prev_hunk);
369 cx.add_action(Editor::go_to_definition);
370 cx.add_action(Editor::go_to_type_definition);
371 cx.add_action(Editor::fold);
372 cx.add_action(Editor::fold_at);
373 cx.add_action(Editor::unfold_lines);
374 cx.add_action(Editor::unfold_at);
375 cx.add_action(Editor::gutter_hover);
376 cx.add_action(Editor::fold_selected_ranges);
377 cx.add_action(Editor::show_completions);
378 cx.add_action(Editor::toggle_code_actions);
379 cx.add_action(Editor::open_excerpts);
380 cx.add_action(Editor::jump);
381 cx.add_action(Editor::toggle_soft_wrap);
382 cx.add_action(Editor::reveal_in_finder);
383 cx.add_action(Editor::copy_highlight_json);
384 cx.add_async_action(Editor::format);
385 cx.add_action(Editor::restart_language_server);
386 cx.add_action(Editor::show_character_palette);
387 cx.add_async_action(Editor::confirm_completion);
388 cx.add_async_action(Editor::confirm_code_action);
389 cx.add_async_action(Editor::rename);
390 cx.add_async_action(Editor::confirm_rename);
391 cx.add_async_action(Editor::find_all_references);
392 cx.add_action(Editor::next_copilot_suggestion);
393 cx.add_action(Editor::previous_copilot_suggestion);
394 cx.add_action(Editor::toggle_copilot_suggestions);
395
396 hover_popover::init(cx);
397 link_go_to_definition::init(cx);
398 mouse_context_menu::init(cx);
399 scroll::actions::init(cx);
400
401 workspace::register_project_item::<Editor>(cx);
402 workspace::register_followable_item::<Editor>(cx);
403 workspace::register_deserializable_item::<Editor>(cx);
404}
405
406trait InvalidationRegion {
407 fn ranges(&self) -> &[Range<Anchor>];
408}
409
410#[derive(Clone, Debug, PartialEq)]
411pub enum SelectPhase {
412 Begin {
413 position: DisplayPoint,
414 add: bool,
415 click_count: usize,
416 },
417 BeginColumnar {
418 position: DisplayPoint,
419 goal_column: u32,
420 },
421 Extend {
422 position: DisplayPoint,
423 click_count: usize,
424 },
425 Update {
426 position: DisplayPoint,
427 goal_column: u32,
428 scroll_position: Vector2F,
429 },
430 End,
431}
432
433#[derive(Clone, Debug)]
434pub enum SelectMode {
435 Character,
436 Word(Range<Anchor>),
437 Line(Range<Anchor>),
438 All,
439}
440
441#[derive(Copy, Clone, PartialEq, Eq, Debug)]
442pub enum EditorMode {
443 SingleLine,
444 AutoHeight { max_lines: usize },
445 Full,
446}
447
448#[derive(Clone)]
449pub enum SoftWrap {
450 None,
451 EditorWidth,
452 Column(u32),
453}
454
455#[derive(Clone)]
456pub struct EditorStyle {
457 pub text: TextStyle,
458 pub placeholder_text: Option<TextStyle>,
459 pub theme: theme::Editor,
460}
461
462type CompletionId = usize;
463
464type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
465type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
466
467pub struct Editor {
468 handle: WeakViewHandle<Self>,
469 buffer: ModelHandle<MultiBuffer>,
470 display_map: ModelHandle<DisplayMap>,
471 pub selections: SelectionsCollection,
472 pub scroll_manager: ScrollManager,
473 columnar_selection_tail: Option<Anchor>,
474 add_selections_state: Option<AddSelectionsState>,
475 select_next_state: Option<SelectNextState>,
476 selection_history: SelectionHistory,
477 autoclose_regions: Vec<AutocloseRegion>,
478 snippet_stack: InvalidationStack<SnippetState>,
479 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
480 ime_transaction: Option<TransactionId>,
481 active_diagnostics: Option<ActiveDiagnosticGroup>,
482 soft_wrap_mode_override: Option<settings::SoftWrap>,
483 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
484 override_text_style: Option<Box<OverrideTextStyle>>,
485 project: Option<ModelHandle<Project>>,
486 focused: bool,
487 blink_manager: ModelHandle<BlinkManager>,
488 show_local_selections: bool,
489 mode: EditorMode,
490 placeholder_text: Option<Arc<str>>,
491 highlighted_rows: Option<Range<u32>>,
492 #[allow(clippy::type_complexity)]
493 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
494 nav_history: Option<ItemNavHistory>,
495 context_menu: Option<ContextMenu>,
496 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
497 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
498 next_completion_id: CompletionId,
499 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
500 code_actions_task: Option<Task<()>>,
501 document_highlights_task: Option<Task<()>>,
502 pending_rename: Option<RenameState>,
503 searchable: bool,
504 cursor_shape: CursorShape,
505 workspace_id: Option<WorkspaceId>,
506 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
507 input_enabled: bool,
508 leader_replica_id: Option<u16>,
509 remote_id: Option<ViewId>,
510 hover_state: HoverState,
511 gutter_hovered: bool,
512 link_go_to_definition_state: LinkGoToDefinitionState,
513 pub copilot_state: CopilotState,
514 _subscriptions: Vec<Subscription>,
515}
516
517pub struct EditorSnapshot {
518 pub mode: EditorMode,
519 pub display_snapshot: DisplaySnapshot,
520 pub placeholder_text: Option<Arc<str>>,
521 is_focused: bool,
522 scroll_anchor: ScrollAnchor,
523 ongoing_scroll: OngoingScroll,
524}
525
526#[derive(Clone, Debug)]
527struct SelectionHistoryEntry {
528 selections: Arc<[Selection<Anchor>]>,
529 select_next_state: Option<SelectNextState>,
530 add_selections_state: Option<AddSelectionsState>,
531}
532
533enum SelectionHistoryMode {
534 Normal,
535 Undoing,
536 Redoing,
537}
538
539impl Default for SelectionHistoryMode {
540 fn default() -> Self {
541 Self::Normal
542 }
543}
544
545#[derive(Default)]
546struct SelectionHistory {
547 #[allow(clippy::type_complexity)]
548 selections_by_transaction:
549 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
550 mode: SelectionHistoryMode,
551 undo_stack: VecDeque<SelectionHistoryEntry>,
552 redo_stack: VecDeque<SelectionHistoryEntry>,
553}
554
555impl SelectionHistory {
556 fn insert_transaction(
557 &mut self,
558 transaction_id: TransactionId,
559 selections: Arc<[Selection<Anchor>]>,
560 ) {
561 self.selections_by_transaction
562 .insert(transaction_id, (selections, None));
563 }
564
565 #[allow(clippy::type_complexity)]
566 fn transaction(
567 &self,
568 transaction_id: TransactionId,
569 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
570 self.selections_by_transaction.get(&transaction_id)
571 }
572
573 #[allow(clippy::type_complexity)]
574 fn transaction_mut(
575 &mut self,
576 transaction_id: TransactionId,
577 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
578 self.selections_by_transaction.get_mut(&transaction_id)
579 }
580
581 fn push(&mut self, entry: SelectionHistoryEntry) {
582 if !entry.selections.is_empty() {
583 match self.mode {
584 SelectionHistoryMode::Normal => {
585 self.push_undo(entry);
586 self.redo_stack.clear();
587 }
588 SelectionHistoryMode::Undoing => self.push_redo(entry),
589 SelectionHistoryMode::Redoing => self.push_undo(entry),
590 }
591 }
592 }
593
594 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
595 if self
596 .undo_stack
597 .back()
598 .map_or(true, |e| e.selections != entry.selections)
599 {
600 self.undo_stack.push_back(entry);
601 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
602 self.undo_stack.pop_front();
603 }
604 }
605 }
606
607 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
608 if self
609 .redo_stack
610 .back()
611 .map_or(true, |e| e.selections != entry.selections)
612 {
613 self.redo_stack.push_back(entry);
614 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
615 self.redo_stack.pop_front();
616 }
617 }
618 }
619}
620
621#[derive(Clone, Debug)]
622struct AddSelectionsState {
623 above: bool,
624 stack: Vec<usize>,
625}
626
627#[derive(Clone, Debug)]
628struct SelectNextState {
629 query: AhoCorasick,
630 wordwise: bool,
631 done: bool,
632}
633
634#[derive(Debug)]
635struct AutocloseRegion {
636 selection_id: usize,
637 range: Range<Anchor>,
638 pair: BracketPair,
639}
640
641#[derive(Debug)]
642struct SnippetState {
643 ranges: Vec<Vec<Range<Anchor>>>,
644 active_index: usize,
645}
646
647pub struct RenameState {
648 pub range: Range<Anchor>,
649 pub old_name: Arc<str>,
650 pub editor: ViewHandle<Editor>,
651 block_id: BlockId,
652}
653
654struct InvalidationStack<T>(Vec<T>);
655
656enum ContextMenu {
657 Completions(CompletionsMenu),
658 CodeActions(CodeActionsMenu),
659}
660
661impl ContextMenu {
662 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
663 if self.visible() {
664 match self {
665 ContextMenu::Completions(menu) => menu.select_first(cx),
666 ContextMenu::CodeActions(menu) => menu.select_first(cx),
667 }
668 true
669 } else {
670 false
671 }
672 }
673
674 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
675 if self.visible() {
676 match self {
677 ContextMenu::Completions(menu) => menu.select_prev(cx),
678 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
679 }
680 true
681 } else {
682 false
683 }
684 }
685
686 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
687 if self.visible() {
688 match self {
689 ContextMenu::Completions(menu) => menu.select_next(cx),
690 ContextMenu::CodeActions(menu) => menu.select_next(cx),
691 }
692 true
693 } else {
694 false
695 }
696 }
697
698 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
699 if self.visible() {
700 match self {
701 ContextMenu::Completions(menu) => menu.select_last(cx),
702 ContextMenu::CodeActions(menu) => menu.select_last(cx),
703 }
704 true
705 } else {
706 false
707 }
708 }
709
710 fn visible(&self) -> bool {
711 match self {
712 ContextMenu::Completions(menu) => menu.visible(),
713 ContextMenu::CodeActions(menu) => menu.visible(),
714 }
715 }
716
717 fn render(
718 &self,
719 cursor_position: DisplayPoint,
720 style: EditorStyle,
721 cx: &mut RenderContext<Editor>,
722 ) -> (DisplayPoint, ElementBox) {
723 match self {
724 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
725 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
726 }
727 }
728}
729
730struct CompletionsMenu {
731 id: CompletionId,
732 initial_position: Anchor,
733 buffer: ModelHandle<Buffer>,
734 completions: Arc<[Completion]>,
735 match_candidates: Vec<StringMatchCandidate>,
736 matches: Arc<[StringMatch]>,
737 selected_item: usize,
738 list: UniformListState,
739}
740
741impl CompletionsMenu {
742 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
743 self.selected_item = 0;
744 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
745 cx.notify();
746 }
747
748 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
749 if self.selected_item > 0 {
750 self.selected_item -= 1;
751 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
752 }
753 cx.notify();
754 }
755
756 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
757 if self.selected_item + 1 < self.matches.len() {
758 self.selected_item += 1;
759 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
760 }
761 cx.notify();
762 }
763
764 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
765 self.selected_item = self.matches.len() - 1;
766 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
767 cx.notify();
768 }
769
770 fn visible(&self) -> bool {
771 !self.matches.is_empty()
772 }
773
774 fn render(&self, style: EditorStyle, cx: &mut RenderContext<Editor>) -> ElementBox {
775 enum CompletionTag {}
776
777 let completions = self.completions.clone();
778 let matches = self.matches.clone();
779 let selected_item = self.selected_item;
780 let container_style = style.autocomplete.container;
781 UniformList::new(
782 self.list.clone(),
783 matches.len(),
784 cx,
785 move |_, range, items, cx| {
786 let start_ix = range.start;
787 for (ix, mat) in matches[range].iter().enumerate() {
788 let completion = &completions[mat.candidate_id];
789 let item_ix = start_ix + ix;
790 items.push(
791 MouseEventHandler::<CompletionTag>::new(
792 mat.candidate_id,
793 cx,
794 |state, _| {
795 let item_style = if item_ix == selected_item {
796 style.autocomplete.selected_item
797 } else if state.hovered() {
798 style.autocomplete.hovered_item
799 } else {
800 style.autocomplete.item
801 };
802
803 Text::new(completion.label.text.clone(), style.text.clone())
804 .with_soft_wrap(false)
805 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
806 &completion.label.text,
807 style.text.color.into(),
808 styled_runs_for_code_label(
809 &completion.label,
810 &style.syntax,
811 ),
812 &mat.positions,
813 ))
814 .contained()
815 .with_style(item_style)
816 .boxed()
817 },
818 )
819 .with_cursor_style(CursorStyle::PointingHand)
820 .on_down(MouseButton::Left, move |_, cx| {
821 cx.dispatch_action(ConfirmCompletion {
822 item_ix: Some(item_ix),
823 });
824 })
825 .boxed(),
826 );
827 }
828 },
829 )
830 .with_width_from_item(
831 self.matches
832 .iter()
833 .enumerate()
834 .max_by_key(|(_, mat)| {
835 self.completions[mat.candidate_id]
836 .label
837 .text
838 .chars()
839 .count()
840 })
841 .map(|(ix, _)| ix),
842 )
843 .contained()
844 .with_style(container_style)
845 .boxed()
846 }
847
848 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
849 let mut matches = if let Some(query) = query {
850 fuzzy::match_strings(
851 &self.match_candidates,
852 query,
853 query.chars().any(|c| c.is_uppercase()),
854 100,
855 &Default::default(),
856 executor,
857 )
858 .await
859 } else {
860 self.match_candidates
861 .iter()
862 .enumerate()
863 .map(|(candidate_id, candidate)| StringMatch {
864 candidate_id,
865 score: Default::default(),
866 positions: Default::default(),
867 string: candidate.string.clone(),
868 })
869 .collect()
870 };
871
872 //Remove all candidates where the query's start does not match the start of any word in the candidate
873 if let Some(query) = query {
874 if let Some(query_start) = query.chars().next() {
875 matches.retain(|string_match| {
876 split_words(&string_match.string).any(|word| {
877 //Check that the first codepoint of the word as lowercase matches the first
878 //codepoint of the query as lowercase
879 word.chars()
880 .flat_map(|codepoint| codepoint.to_lowercase())
881 .zip(query_start.to_lowercase())
882 .all(|(word_cp, query_cp)| word_cp == query_cp)
883 })
884 });
885 }
886 }
887
888 matches.sort_unstable_by_key(|mat| {
889 let completion = &self.completions[mat.candidate_id];
890 (
891 completion.lsp_completion.sort_text.as_ref(),
892 Reverse(OrderedFloat(mat.score)),
893 completion.sort_key(),
894 )
895 });
896
897 for mat in &mut matches {
898 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
899 for position in &mut mat.positions {
900 *position += filter_start;
901 }
902 }
903
904 self.matches = matches.into();
905 }
906}
907
908#[derive(Clone)]
909struct CodeActionsMenu {
910 actions: Arc<[CodeAction]>,
911 buffer: ModelHandle<Buffer>,
912 selected_item: usize,
913 list: UniformListState,
914 deployed_from_indicator: bool,
915}
916
917impl CodeActionsMenu {
918 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
919 self.selected_item = 0;
920 cx.notify()
921 }
922
923 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
924 if self.selected_item > 0 {
925 self.selected_item -= 1;
926 cx.notify()
927 }
928 }
929
930 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
931 if self.selected_item + 1 < self.actions.len() {
932 self.selected_item += 1;
933 cx.notify()
934 }
935 }
936
937 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
938 self.selected_item = self.actions.len() - 1;
939 cx.notify()
940 }
941
942 fn visible(&self) -> bool {
943 !self.actions.is_empty()
944 }
945
946 fn render(
947 &self,
948 mut cursor_position: DisplayPoint,
949 style: EditorStyle,
950 cx: &mut RenderContext<Editor>,
951 ) -> (DisplayPoint, ElementBox) {
952 enum ActionTag {}
953
954 let container_style = style.autocomplete.container;
955 let actions = self.actions.clone();
956 let selected_item = self.selected_item;
957 let element = UniformList::new(
958 self.list.clone(),
959 actions.len(),
960 cx,
961 move |_, range, items, cx| {
962 let start_ix = range.start;
963 for (ix, action) in actions[range].iter().enumerate() {
964 let item_ix = start_ix + ix;
965 items.push(
966 MouseEventHandler::<ActionTag>::new(item_ix, cx, |state, _| {
967 let item_style = if item_ix == selected_item {
968 style.autocomplete.selected_item
969 } else if state.hovered() {
970 style.autocomplete.hovered_item
971 } else {
972 style.autocomplete.item
973 };
974
975 Text::new(action.lsp_action.title.clone(), style.text.clone())
976 .with_soft_wrap(false)
977 .contained()
978 .with_style(item_style)
979 .boxed()
980 })
981 .with_cursor_style(CursorStyle::PointingHand)
982 .on_down(MouseButton::Left, move |_, cx| {
983 cx.dispatch_action(ConfirmCodeAction {
984 item_ix: Some(item_ix),
985 });
986 })
987 .boxed(),
988 );
989 }
990 },
991 )
992 .with_width_from_item(
993 self.actions
994 .iter()
995 .enumerate()
996 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
997 .map(|(ix, _)| ix),
998 )
999 .contained()
1000 .with_style(container_style)
1001 .boxed();
1002
1003 if self.deployed_from_indicator {
1004 *cursor_position.column_mut() = 0;
1005 }
1006
1007 (cursor_position, element)
1008 }
1009}
1010
1011pub struct CopilotState {
1012 excerpt_id: Option<ExcerptId>,
1013 pending_refresh: Task<Option<()>>,
1014 completions: Vec<copilot::Completion>,
1015 active_completion_index: usize,
1016 pub user_enabled: Option<bool>,
1017}
1018
1019impl Default for CopilotState {
1020 fn default() -> Self {
1021 Self {
1022 excerpt_id: None,
1023 pending_refresh: Task::ready(Some(())),
1024 completions: Default::default(),
1025 active_completion_index: 0,
1026 user_enabled: None,
1027 }
1028 }
1029}
1030
1031impl CopilotState {
1032 fn text_for_active_completion(
1033 &self,
1034 cursor: Anchor,
1035 buffer: &MultiBufferSnapshot,
1036 ) -> Option<&str> {
1037 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 cx.notify();
2863 self.copilot_state.user_enabled = Some(true);
2864 }
2865
2866 if self.copilot_state.completions.is_empty() {
2867 self.refresh_copilot_suggestions(cx);
2868 return;
2869 }
2870
2871 self.copilot_state.active_completion_index =
2872 (self.copilot_state.active_completion_index + 1) % self.copilot_state.completions.len();
2873
2874 self.sync_suggestion(cx);
2875 }
2876
2877 fn previous_copilot_suggestion(
2878 &mut self,
2879 _: &copilot::PreviousSuggestion,
2880 cx: &mut ViewContext<Self>,
2881 ) {
2882 // Auto re-enable copilot if you're asking for a suggestion
2883 if self.copilot_state.user_enabled == Some(false) {
2884 cx.notify();
2885 self.copilot_state.user_enabled = Some(true);
2886 }
2887
2888 if self.copilot_state.completions.is_empty() {
2889 self.refresh_copilot_suggestions(cx);
2890 return;
2891 }
2892
2893 self.copilot_state.active_completion_index =
2894 if self.copilot_state.active_completion_index == 0 {
2895 self.copilot_state.completions.len() - 1
2896 } else {
2897 self.copilot_state.active_completion_index - 1
2898 };
2899
2900 self.sync_suggestion(cx);
2901 }
2902
2903 fn toggle_copilot_suggestions(&mut self, _: &copilot::Toggle, cx: &mut ViewContext<Self>) {
2904 self.copilot_state.user_enabled = match self.copilot_state.user_enabled {
2905 Some(enabled) => Some(!enabled),
2906 None => {
2907 let selection = self.selections.newest_anchor().start;
2908
2909 let language_name = self
2910 .snapshot(cx)
2911 .language_at(selection)
2912 .map(|language| language.name());
2913
2914 let copilot_enabled = cx.global::<Settings>().copilot_on(language_name.as_deref());
2915
2916 Some(!copilot_enabled)
2917 }
2918 };
2919
2920 // We know this can't be None, as we just set it to Some above
2921 if self.copilot_state.user_enabled == Some(true) {
2922 self.refresh_copilot_suggestions(cx);
2923 } else {
2924 self.clear_copilot_suggestions(cx);
2925 }
2926
2927 cx.notify();
2928 }
2929
2930 fn sync_suggestion(&mut self, cx: &mut ViewContext<Self>) {
2931 let snapshot = self.buffer.read(cx).snapshot(cx);
2932 let cursor = self.selections.newest_anchor().head();
2933
2934 if let Some(text) = self
2935 .copilot_state
2936 .text_for_active_completion(cursor, &snapshot)
2937 {
2938 self.display_map.update(cx, |map, cx| {
2939 map.replace_suggestion(
2940 Some(Suggestion {
2941 position: cursor,
2942 text: text.into(),
2943 }),
2944 cx,
2945 )
2946 });
2947 cx.notify();
2948 }
2949 }
2950
2951 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
2952 let snapshot = self.buffer.read(cx).snapshot(cx);
2953 let cursor = self.selections.newest_anchor().head();
2954 if let Some(text) = self
2955 .copilot_state
2956 .text_for_active_completion(cursor, &snapshot)
2957 {
2958 self.insert(&text.to_string(), cx);
2959 self.clear_copilot_suggestions(cx);
2960 true
2961 } else {
2962 false
2963 }
2964 }
2965
2966 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) -> bool {
2967 self.display_map
2968 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
2969 let was_empty = self.copilot_state.completions.is_empty();
2970 self.copilot_state.completions.clear();
2971 self.copilot_state.active_completion_index = 0;
2972 self.copilot_state.pending_refresh = Task::ready(None);
2973 self.copilot_state.excerpt_id = None;
2974 cx.notify();
2975 !was_empty
2976 }
2977
2978 pub fn render_code_actions_indicator(
2979 &self,
2980 style: &EditorStyle,
2981 active: bool,
2982 cx: &mut RenderContext<Self>,
2983 ) -> Option<ElementBox> {
2984 if self.available_code_actions.is_some() {
2985 enum CodeActions {}
2986 Some(
2987 MouseEventHandler::<CodeActions>::new(0, cx, |state, _| {
2988 Svg::new("icons/bolt_8.svg")
2989 .with_color(style.code_actions.indicator.style_for(state, active).color)
2990 .boxed()
2991 })
2992 .with_cursor_style(CursorStyle::PointingHand)
2993 .with_padding(Padding::uniform(3.))
2994 .on_down(MouseButton::Left, |_, cx| {
2995 cx.dispatch_action(ToggleCodeActions {
2996 deployed_from_indicator: true,
2997 });
2998 })
2999 .boxed(),
3000 )
3001 } else {
3002 None
3003 }
3004 }
3005
3006 pub fn render_fold_indicators(
3007 &self,
3008 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3009 style: &EditorStyle,
3010 gutter_hovered: bool,
3011 line_height: f32,
3012 gutter_margin: f32,
3013 cx: &mut RenderContext<Self>,
3014 ) -> Vec<Option<ElementBox>> {
3015 enum FoldIndicators {}
3016
3017 let style = style.folds.clone();
3018
3019 fold_data
3020 .iter()
3021 .enumerate()
3022 .map(|(ix, fold_data)| {
3023 fold_data
3024 .map(|(fold_status, buffer_row, active)| {
3025 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3026 MouseEventHandler::<FoldIndicators>::new(
3027 ix as usize,
3028 cx,
3029 |mouse_state, _| -> ElementBox {
3030 Svg::new(match fold_status {
3031 FoldStatus::Folded => style.folded_icon.clone(),
3032 FoldStatus::Foldable => style.foldable_icon.clone(),
3033 })
3034 .with_color(
3035 style
3036 .indicator
3037 .style_for(
3038 mouse_state,
3039 fold_status == FoldStatus::Folded,
3040 )
3041 .color,
3042 )
3043 .constrained()
3044 .with_width(gutter_margin * style.icon_margin_scale)
3045 .aligned()
3046 .constrained()
3047 .with_height(line_height)
3048 .with_width(gutter_margin)
3049 .aligned()
3050 .boxed()
3051 },
3052 )
3053 .with_cursor_style(CursorStyle::PointingHand)
3054 .with_padding(Padding::uniform(3.))
3055 .on_click(MouseButton::Left, {
3056 move |_, cx| {
3057 cx.dispatch_any_action(match fold_status {
3058 FoldStatus::Folded => Box::new(UnfoldAt { buffer_row }),
3059 FoldStatus::Foldable => Box::new(FoldAt { buffer_row }),
3060 });
3061 }
3062 })
3063 .boxed()
3064 })
3065 })
3066 .flatten()
3067 })
3068 .collect()
3069 }
3070
3071 pub fn context_menu_visible(&self) -> bool {
3072 self.context_menu
3073 .as_ref()
3074 .map_or(false, |menu| menu.visible())
3075 }
3076
3077 pub fn render_context_menu(
3078 &self,
3079 cursor_position: DisplayPoint,
3080 style: EditorStyle,
3081 cx: &mut RenderContext<Editor>,
3082 ) -> Option<(DisplayPoint, ElementBox)> {
3083 self.context_menu
3084 .as_ref()
3085 .map(|menu| menu.render(cursor_position, style, cx))
3086 }
3087
3088 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3089 if !matches!(menu, ContextMenu::Completions(_)) {
3090 self.completion_tasks.clear();
3091 }
3092 self.context_menu = Some(menu);
3093 cx.notify();
3094 }
3095
3096 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3097 cx.notify();
3098 self.completion_tasks.clear();
3099 self.context_menu.take()
3100 }
3101
3102 pub fn insert_snippet(
3103 &mut self,
3104 insertion_ranges: &[Range<usize>],
3105 snippet: Snippet,
3106 cx: &mut ViewContext<Self>,
3107 ) -> Result<()> {
3108 let tabstops = self.buffer.update(cx, |buffer, cx| {
3109 let snippet_text: Arc<str> = snippet.text.clone().into();
3110 buffer.edit(
3111 insertion_ranges
3112 .iter()
3113 .cloned()
3114 .map(|range| (range, snippet_text.clone())),
3115 Some(AutoindentMode::EachLine),
3116 cx,
3117 );
3118
3119 let snapshot = &*buffer.read(cx);
3120 let snippet = &snippet;
3121 snippet
3122 .tabstops
3123 .iter()
3124 .map(|tabstop| {
3125 let mut tabstop_ranges = tabstop
3126 .iter()
3127 .flat_map(|tabstop_range| {
3128 let mut delta = 0_isize;
3129 insertion_ranges.iter().map(move |insertion_range| {
3130 let insertion_start = insertion_range.start as isize + delta;
3131 delta +=
3132 snippet.text.len() as isize - insertion_range.len() as isize;
3133
3134 let start = snapshot.anchor_before(
3135 (insertion_start + tabstop_range.start) as usize,
3136 );
3137 let end = snapshot
3138 .anchor_after((insertion_start + tabstop_range.end) as usize);
3139 start..end
3140 })
3141 })
3142 .collect::<Vec<_>>();
3143 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3144 tabstop_ranges
3145 })
3146 .collect::<Vec<_>>()
3147 });
3148
3149 if let Some(tabstop) = tabstops.first() {
3150 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3151 s.select_ranges(tabstop.iter().cloned());
3152 });
3153 self.snippet_stack.push(SnippetState {
3154 active_index: 0,
3155 ranges: tabstops,
3156 });
3157 }
3158
3159 Ok(())
3160 }
3161
3162 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3163 self.move_to_snippet_tabstop(Bias::Right, cx)
3164 }
3165
3166 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3167 self.move_to_snippet_tabstop(Bias::Left, cx)
3168 }
3169
3170 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3171 if let Some(mut snippet) = self.snippet_stack.pop() {
3172 match bias {
3173 Bias::Left => {
3174 if snippet.active_index > 0 {
3175 snippet.active_index -= 1;
3176 } else {
3177 self.snippet_stack.push(snippet);
3178 return false;
3179 }
3180 }
3181 Bias::Right => {
3182 if snippet.active_index + 1 < snippet.ranges.len() {
3183 snippet.active_index += 1;
3184 } else {
3185 self.snippet_stack.push(snippet);
3186 return false;
3187 }
3188 }
3189 }
3190 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3191 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3192 s.select_anchor_ranges(current_ranges.iter().cloned())
3193 });
3194 // If snippet state is not at the last tabstop, push it back on the stack
3195 if snippet.active_index + 1 < snippet.ranges.len() {
3196 self.snippet_stack.push(snippet);
3197 }
3198 return true;
3199 }
3200 }
3201
3202 false
3203 }
3204
3205 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3206 self.transact(cx, |this, cx| {
3207 this.select_all(&SelectAll, cx);
3208 this.insert("", cx);
3209 });
3210 }
3211
3212 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3213 self.transact(cx, |this, cx| {
3214 this.select_autoclose_pair(cx);
3215 let mut selections = this.selections.all::<Point>(cx);
3216 if !this.selections.line_mode {
3217 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3218 for selection in &mut selections {
3219 if selection.is_empty() {
3220 let old_head = selection.head();
3221 let mut new_head =
3222 movement::left(&display_map, old_head.to_display_point(&display_map))
3223 .to_point(&display_map);
3224 if let Some((buffer, line_buffer_range)) = display_map
3225 .buffer_snapshot
3226 .buffer_line_for_row(old_head.row)
3227 {
3228 let indent_size =
3229 buffer.indent_size_for_line(line_buffer_range.start.row);
3230 let language_name = buffer
3231 .language_at(line_buffer_range.start)
3232 .map(|language| language.name());
3233 let indent_len = match indent_size.kind {
3234 IndentKind::Space => {
3235 cx.global::<Settings>().tab_size(language_name.as_deref())
3236 }
3237 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3238 };
3239 if old_head.column <= indent_size.len && old_head.column > 0 {
3240 let indent_len = indent_len.get();
3241 new_head = cmp::min(
3242 new_head,
3243 Point::new(
3244 old_head.row,
3245 ((old_head.column - 1) / indent_len) * indent_len,
3246 ),
3247 );
3248 }
3249 }
3250
3251 selection.set_head(new_head, SelectionGoal::None);
3252 }
3253 }
3254 }
3255
3256 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3257 this.insert("", cx);
3258 });
3259 }
3260
3261 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3262 self.transact(cx, |this, cx| {
3263 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3264 let line_mode = s.line_mode;
3265 s.move_with(|map, selection| {
3266 if selection.is_empty() && !line_mode {
3267 let cursor = movement::right(map, selection.head());
3268 selection.set_head(cursor, SelectionGoal::None);
3269 }
3270 })
3271 });
3272 this.insert("", cx);
3273 });
3274 }
3275
3276 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3277 if self.move_to_prev_snippet_tabstop(cx) {
3278 return;
3279 }
3280
3281 self.outdent(&Outdent, cx);
3282 }
3283
3284 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3285 if self.accept_copilot_suggestion(cx) {
3286 return;
3287 }
3288
3289 if self.move_to_next_snippet_tabstop(cx) {
3290 return;
3291 }
3292
3293 let mut selections = self.selections.all_adjusted(cx);
3294 let buffer = self.buffer.read(cx);
3295 let snapshot = buffer.snapshot(cx);
3296 let rows_iter = selections.iter().map(|s| s.head().row);
3297 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3298
3299 let mut edits = Vec::new();
3300 let mut prev_edited_row = 0;
3301 let mut row_delta = 0;
3302 for selection in &mut selections {
3303 if selection.start.row != prev_edited_row {
3304 row_delta = 0;
3305 }
3306 prev_edited_row = selection.end.row;
3307
3308 // If the selection is non-empty, then increase the indentation of the selected lines.
3309 if !selection.is_empty() {
3310 row_delta =
3311 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3312 continue;
3313 }
3314
3315 // If the selection is empty and the cursor is in the leading whitespace before the
3316 // suggested indentation, then auto-indent the line.
3317 let cursor = selection.head();
3318 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3319 let current_indent = snapshot.indent_size_for_line(cursor.row);
3320 if cursor.column < suggested_indent.len
3321 && cursor.column <= current_indent.len
3322 && current_indent.len <= suggested_indent.len
3323 {
3324 selection.start = Point::new(cursor.row, suggested_indent.len);
3325 selection.end = selection.start;
3326 if row_delta == 0 {
3327 edits.extend(Buffer::edit_for_indent_size_adjustment(
3328 cursor.row,
3329 current_indent,
3330 suggested_indent,
3331 ));
3332 row_delta = suggested_indent.len - current_indent.len;
3333 }
3334 continue;
3335 }
3336 }
3337
3338 // Otherwise, insert a hard or soft tab.
3339 let settings = cx.global::<Settings>();
3340 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3341 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3342 IndentSize::tab()
3343 } else {
3344 let tab_size = settings.tab_size(language_name.as_deref()).get();
3345 let char_column = snapshot
3346 .text_for_range(Point::new(cursor.row, 0)..cursor)
3347 .flat_map(str::chars)
3348 .count()
3349 + row_delta as usize;
3350 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3351 IndentSize::spaces(chars_to_next_tab_stop)
3352 };
3353 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3354 selection.end = selection.start;
3355 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3356 row_delta += tab_size.len;
3357 }
3358
3359 self.transact(cx, |this, cx| {
3360 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3361 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections))
3362 });
3363 }
3364
3365 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3366 let mut selections = self.selections.all::<Point>(cx);
3367 let mut prev_edited_row = 0;
3368 let mut row_delta = 0;
3369 let mut edits = Vec::new();
3370 let buffer = self.buffer.read(cx);
3371 let snapshot = buffer.snapshot(cx);
3372 for selection in &mut selections {
3373 if selection.start.row != prev_edited_row {
3374 row_delta = 0;
3375 }
3376 prev_edited_row = selection.end.row;
3377
3378 row_delta =
3379 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3380 }
3381
3382 self.transact(cx, |this, cx| {
3383 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3384 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3385 });
3386 }
3387
3388 fn indent_selection(
3389 buffer: &MultiBuffer,
3390 snapshot: &MultiBufferSnapshot,
3391 selection: &mut Selection<Point>,
3392 edits: &mut Vec<(Range<Point>, String)>,
3393 delta_for_start_row: u32,
3394 cx: &AppContext,
3395 ) -> u32 {
3396 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3397 let settings = cx.global::<Settings>();
3398 let tab_size = settings.tab_size(language_name.as_deref()).get();
3399 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3400 IndentKind::Tab
3401 } else {
3402 IndentKind::Space
3403 };
3404 let mut start_row = selection.start.row;
3405 let mut end_row = selection.end.row + 1;
3406
3407 // If a selection ends at the beginning of a line, don't indent
3408 // that last line.
3409 if selection.end.column == 0 {
3410 end_row -= 1;
3411 }
3412
3413 // Avoid re-indenting a row that has already been indented by a
3414 // previous selection, but still update this selection's column
3415 // to reflect that indentation.
3416 if delta_for_start_row > 0 {
3417 start_row += 1;
3418 selection.start.column += delta_for_start_row;
3419 if selection.end.row == selection.start.row {
3420 selection.end.column += delta_for_start_row;
3421 }
3422 }
3423
3424 let mut delta_for_end_row = 0;
3425 for row in start_row..end_row {
3426 let current_indent = snapshot.indent_size_for_line(row);
3427 let indent_delta = match (current_indent.kind, indent_kind) {
3428 (IndentKind::Space, IndentKind::Space) => {
3429 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3430 IndentSize::spaces(columns_to_next_tab_stop)
3431 }
3432 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3433 (_, IndentKind::Tab) => IndentSize::tab(),
3434 };
3435
3436 let row_start = Point::new(row, 0);
3437 edits.push((
3438 row_start..row_start,
3439 indent_delta.chars().collect::<String>(),
3440 ));
3441
3442 // Update this selection's endpoints to reflect the indentation.
3443 if row == selection.start.row {
3444 selection.start.column += indent_delta.len;
3445 }
3446 if row == selection.end.row {
3447 selection.end.column += indent_delta.len;
3448 delta_for_end_row = indent_delta.len;
3449 }
3450 }
3451
3452 if selection.start.row == selection.end.row {
3453 delta_for_start_row + delta_for_end_row
3454 } else {
3455 delta_for_end_row
3456 }
3457 }
3458
3459 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3460 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3461 let selections = self.selections.all::<Point>(cx);
3462 let mut deletion_ranges = Vec::new();
3463 let mut last_outdent = None;
3464 {
3465 let buffer = self.buffer.read(cx);
3466 let snapshot = buffer.snapshot(cx);
3467 for selection in &selections {
3468 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3469 let tab_size = cx
3470 .global::<Settings>()
3471 .tab_size(language_name.as_deref())
3472 .get();
3473 let mut rows = selection.spanned_rows(false, &display_map);
3474
3475 // Avoid re-outdenting a row that has already been outdented by a
3476 // previous selection.
3477 if let Some(last_row) = last_outdent {
3478 if last_row == rows.start {
3479 rows.start += 1;
3480 }
3481 }
3482
3483 for row in rows {
3484 let indent_size = snapshot.indent_size_for_line(row);
3485 if indent_size.len > 0 {
3486 let deletion_len = match indent_size.kind {
3487 IndentKind::Space => {
3488 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3489 if columns_to_prev_tab_stop == 0 {
3490 tab_size
3491 } else {
3492 columns_to_prev_tab_stop
3493 }
3494 }
3495 IndentKind::Tab => 1,
3496 };
3497 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3498 last_outdent = Some(row);
3499 }
3500 }
3501 }
3502 }
3503
3504 self.transact(cx, |this, cx| {
3505 this.buffer.update(cx, |buffer, cx| {
3506 let empty_str: Arc<str> = "".into();
3507 buffer.edit(
3508 deletion_ranges
3509 .into_iter()
3510 .map(|range| (range, empty_str.clone())),
3511 None,
3512 cx,
3513 );
3514 });
3515 let selections = this.selections.all::<usize>(cx);
3516 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3517 });
3518 }
3519
3520 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3521 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3522 let selections = self.selections.all::<Point>(cx);
3523
3524 let mut new_cursors = Vec::new();
3525 let mut edit_ranges = Vec::new();
3526 let mut selections = selections.iter().peekable();
3527 while let Some(selection) = selections.next() {
3528 let mut rows = selection.spanned_rows(false, &display_map);
3529 let goal_display_column = selection.head().to_display_point(&display_map).column();
3530
3531 // Accumulate contiguous regions of rows that we want to delete.
3532 while let Some(next_selection) = selections.peek() {
3533 let next_rows = next_selection.spanned_rows(false, &display_map);
3534 if next_rows.start <= rows.end {
3535 rows.end = next_rows.end;
3536 selections.next().unwrap();
3537 } else {
3538 break;
3539 }
3540 }
3541
3542 let buffer = &display_map.buffer_snapshot;
3543 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3544 let edit_end;
3545 let cursor_buffer_row;
3546 if buffer.max_point().row >= rows.end {
3547 // If there's a line after the range, delete the \n from the end of the row range
3548 // and position the cursor on the next line.
3549 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3550 cursor_buffer_row = rows.end;
3551 } else {
3552 // If there isn't a line after the range, delete the \n from the line before the
3553 // start of the row range and position the cursor there.
3554 edit_start = edit_start.saturating_sub(1);
3555 edit_end = buffer.len();
3556 cursor_buffer_row = rows.start.saturating_sub(1);
3557 }
3558
3559 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3560 *cursor.column_mut() =
3561 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3562
3563 new_cursors.push((
3564 selection.id,
3565 buffer.anchor_after(cursor.to_point(&display_map)),
3566 ));
3567 edit_ranges.push(edit_start..edit_end);
3568 }
3569
3570 self.transact(cx, |this, cx| {
3571 let buffer = this.buffer.update(cx, |buffer, cx| {
3572 let empty_str: Arc<str> = "".into();
3573 buffer.edit(
3574 edit_ranges
3575 .into_iter()
3576 .map(|range| (range, empty_str.clone())),
3577 None,
3578 cx,
3579 );
3580 buffer.snapshot(cx)
3581 });
3582 let new_selections = new_cursors
3583 .into_iter()
3584 .map(|(id, cursor)| {
3585 let cursor = cursor.to_point(&buffer);
3586 Selection {
3587 id,
3588 start: cursor,
3589 end: cursor,
3590 reversed: false,
3591 goal: SelectionGoal::None,
3592 }
3593 })
3594 .collect();
3595
3596 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3597 s.select(new_selections);
3598 });
3599 });
3600 }
3601
3602 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3603 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3604 let buffer = &display_map.buffer_snapshot;
3605 let selections = self.selections.all::<Point>(cx);
3606
3607 let mut edits = Vec::new();
3608 let mut selections_iter = selections.iter().peekable();
3609 while let Some(selection) = selections_iter.next() {
3610 // Avoid duplicating the same lines twice.
3611 let mut rows = selection.spanned_rows(false, &display_map);
3612
3613 while let Some(next_selection) = selections_iter.peek() {
3614 let next_rows = next_selection.spanned_rows(false, &display_map);
3615 if next_rows.start < rows.end {
3616 rows.end = next_rows.end;
3617 selections_iter.next().unwrap();
3618 } else {
3619 break;
3620 }
3621 }
3622
3623 // Copy the text from the selected row region and splice it at the start of the region.
3624 let start = Point::new(rows.start, 0);
3625 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3626 let text = buffer
3627 .text_for_range(start..end)
3628 .chain(Some("\n"))
3629 .collect::<String>();
3630 edits.push((start..start, text));
3631 }
3632
3633 self.transact(cx, |this, cx| {
3634 this.buffer.update(cx, |buffer, cx| {
3635 buffer.edit(edits, None, cx);
3636 });
3637
3638 this.request_autoscroll(Autoscroll::fit(), cx);
3639 });
3640 }
3641
3642 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3643 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3644 let buffer = self.buffer.read(cx).snapshot(cx);
3645
3646 let mut edits = Vec::new();
3647 let mut unfold_ranges = Vec::new();
3648 let mut refold_ranges = Vec::new();
3649
3650 let selections = self.selections.all::<Point>(cx);
3651 let mut selections = selections.iter().peekable();
3652 let mut contiguous_row_selections = Vec::new();
3653 let mut new_selections = Vec::new();
3654
3655 while let Some(selection) = selections.next() {
3656 // Find all the selections that span a contiguous row range
3657 let (start_row, end_row) = consume_contiguous_rows(
3658 &mut contiguous_row_selections,
3659 selection,
3660 &display_map,
3661 &mut selections,
3662 );
3663
3664 // Move the text spanned by the row range to be before the line preceding the row range
3665 if start_row > 0 {
3666 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3667 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3668 let insertion_point = display_map
3669 .prev_line_boundary(Point::new(start_row - 1, 0))
3670 .0;
3671
3672 // Don't move lines across excerpts
3673 if buffer
3674 .excerpt_boundaries_in_range((
3675 Bound::Excluded(insertion_point),
3676 Bound::Included(range_to_move.end),
3677 ))
3678 .next()
3679 .is_none()
3680 {
3681 let text = buffer
3682 .text_for_range(range_to_move.clone())
3683 .flat_map(|s| s.chars())
3684 .skip(1)
3685 .chain(['\n'])
3686 .collect::<String>();
3687
3688 edits.push((
3689 buffer.anchor_after(range_to_move.start)
3690 ..buffer.anchor_before(range_to_move.end),
3691 String::new(),
3692 ));
3693 let insertion_anchor = buffer.anchor_after(insertion_point);
3694 edits.push((insertion_anchor..insertion_anchor, text));
3695
3696 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3697
3698 // Move selections up
3699 new_selections.extend(contiguous_row_selections.drain(..).map(
3700 |mut selection| {
3701 selection.start.row -= row_delta;
3702 selection.end.row -= row_delta;
3703 selection
3704 },
3705 ));
3706
3707 // Move folds up
3708 unfold_ranges.push(range_to_move.clone());
3709 for fold in display_map.folds_in_range(
3710 buffer.anchor_before(range_to_move.start)
3711 ..buffer.anchor_after(range_to_move.end),
3712 ) {
3713 let mut start = fold.start.to_point(&buffer);
3714 let mut end = fold.end.to_point(&buffer);
3715 start.row -= row_delta;
3716 end.row -= row_delta;
3717 refold_ranges.push(start..end);
3718 }
3719 }
3720 }
3721
3722 // If we didn't move line(s), preserve the existing selections
3723 new_selections.append(&mut contiguous_row_selections);
3724 }
3725
3726 self.transact(cx, |this, cx| {
3727 this.unfold_ranges(unfold_ranges, true, true, cx);
3728 this.buffer.update(cx, |buffer, cx| {
3729 for (range, text) in edits {
3730 buffer.edit([(range, text)], None, cx);
3731 }
3732 });
3733 this.fold_ranges(refold_ranges, true, cx);
3734 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3735 s.select(new_selections);
3736 })
3737 });
3738 }
3739
3740 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3741 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3742 let buffer = self.buffer.read(cx).snapshot(cx);
3743
3744 let mut edits = Vec::new();
3745 let mut unfold_ranges = Vec::new();
3746 let mut refold_ranges = Vec::new();
3747
3748 let selections = self.selections.all::<Point>(cx);
3749 let mut selections = selections.iter().peekable();
3750 let mut contiguous_row_selections = Vec::new();
3751 let mut new_selections = Vec::new();
3752
3753 while let Some(selection) = selections.next() {
3754 // Find all the selections that span a contiguous row range
3755 let (start_row, end_row) = consume_contiguous_rows(
3756 &mut contiguous_row_selections,
3757 selection,
3758 &display_map,
3759 &mut selections,
3760 );
3761
3762 // Move the text spanned by the row range to be after the last line of the row range
3763 if end_row <= buffer.max_point().row {
3764 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3765 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3766
3767 // Don't move lines across excerpt boundaries
3768 if buffer
3769 .excerpt_boundaries_in_range((
3770 Bound::Excluded(range_to_move.start),
3771 Bound::Included(insertion_point),
3772 ))
3773 .next()
3774 .is_none()
3775 {
3776 let mut text = String::from("\n");
3777 text.extend(buffer.text_for_range(range_to_move.clone()));
3778 text.pop(); // Drop trailing newline
3779 edits.push((
3780 buffer.anchor_after(range_to_move.start)
3781 ..buffer.anchor_before(range_to_move.end),
3782 String::new(),
3783 ));
3784 let insertion_anchor = buffer.anchor_after(insertion_point);
3785 edits.push((insertion_anchor..insertion_anchor, text));
3786
3787 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3788
3789 // Move selections down
3790 new_selections.extend(contiguous_row_selections.drain(..).map(
3791 |mut selection| {
3792 selection.start.row += row_delta;
3793 selection.end.row += row_delta;
3794 selection
3795 },
3796 ));
3797
3798 // Move folds down
3799 unfold_ranges.push(range_to_move.clone());
3800 for fold in display_map.folds_in_range(
3801 buffer.anchor_before(range_to_move.start)
3802 ..buffer.anchor_after(range_to_move.end),
3803 ) {
3804 let mut start = fold.start.to_point(&buffer);
3805 let mut end = fold.end.to_point(&buffer);
3806 start.row += row_delta;
3807 end.row += row_delta;
3808 refold_ranges.push(start..end);
3809 }
3810 }
3811 }
3812
3813 // If we didn't move line(s), preserve the existing selections
3814 new_selections.append(&mut contiguous_row_selections);
3815 }
3816
3817 self.transact(cx, |this, cx| {
3818 this.unfold_ranges(unfold_ranges, true, true, cx);
3819 this.buffer.update(cx, |buffer, cx| {
3820 for (range, text) in edits {
3821 buffer.edit([(range, text)], None, cx);
3822 }
3823 });
3824 this.fold_ranges(refold_ranges, true, cx);
3825 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3826 });
3827 }
3828
3829 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3830 self.transact(cx, |this, cx| {
3831 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3832 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3833 let line_mode = s.line_mode;
3834 s.move_with(|display_map, selection| {
3835 if !selection.is_empty() || line_mode {
3836 return;
3837 }
3838
3839 let mut head = selection.head();
3840 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3841 if head.column() == display_map.line_len(head.row()) {
3842 transpose_offset = display_map
3843 .buffer_snapshot
3844 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3845 }
3846
3847 if transpose_offset == 0 {
3848 return;
3849 }
3850
3851 *head.column_mut() += 1;
3852 head = display_map.clip_point(head, Bias::Right);
3853 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3854
3855 let transpose_start = display_map
3856 .buffer_snapshot
3857 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3858 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3859 let transpose_end = display_map
3860 .buffer_snapshot
3861 .clip_offset(transpose_offset + 1, Bias::Right);
3862 if let Some(ch) =
3863 display_map.buffer_snapshot.chars_at(transpose_start).next()
3864 {
3865 edits.push((transpose_start..transpose_offset, String::new()));
3866 edits.push((transpose_end..transpose_end, ch.to_string()));
3867 }
3868 }
3869 });
3870 edits
3871 });
3872 this.buffer
3873 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3874 let selections = this.selections.all::<usize>(cx);
3875 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3876 s.select(selections);
3877 });
3878 });
3879 }
3880
3881 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3882 let mut text = String::new();
3883 let buffer = self.buffer.read(cx).snapshot(cx);
3884 let mut selections = self.selections.all::<Point>(cx);
3885 let mut clipboard_selections = Vec::with_capacity(selections.len());
3886 {
3887 let max_point = buffer.max_point();
3888 for selection in &mut selections {
3889 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3890 if is_entire_line {
3891 selection.start = Point::new(selection.start.row, 0);
3892 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3893 selection.goal = SelectionGoal::None;
3894 }
3895 let mut len = 0;
3896 for chunk in buffer.text_for_range(selection.start..selection.end) {
3897 text.push_str(chunk);
3898 len += chunk.len();
3899 }
3900 clipboard_selections.push(ClipboardSelection {
3901 len,
3902 is_entire_line,
3903 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
3904 });
3905 }
3906 }
3907
3908 self.transact(cx, |this, cx| {
3909 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3910 s.select(selections);
3911 });
3912 this.insert("", cx);
3913 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3914 });
3915 }
3916
3917 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3918 let selections = self.selections.all::<Point>(cx);
3919 let buffer = self.buffer.read(cx).read(cx);
3920 let mut text = String::new();
3921
3922 let mut clipboard_selections = Vec::with_capacity(selections.len());
3923 {
3924 let max_point = buffer.max_point();
3925 for selection in selections.iter() {
3926 let mut start = selection.start;
3927 let mut end = selection.end;
3928 let is_entire_line = selection.is_empty() || self.selections.line_mode;
3929 if is_entire_line {
3930 start = Point::new(start.row, 0);
3931 end = cmp::min(max_point, Point::new(end.row + 1, 0));
3932 }
3933 let mut len = 0;
3934 for chunk in buffer.text_for_range(start..end) {
3935 text.push_str(chunk);
3936 len += chunk.len();
3937 }
3938 clipboard_selections.push(ClipboardSelection {
3939 len,
3940 is_entire_line,
3941 first_line_indent: buffer.indent_size_for_line(start.row).len,
3942 });
3943 }
3944 }
3945
3946 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3947 }
3948
3949 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3950 self.transact(cx, |this, cx| {
3951 if let Some(item) = cx.as_mut().read_from_clipboard() {
3952 let mut clipboard_text = Cow::Borrowed(item.text());
3953 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3954 let old_selections = this.selections.all::<usize>(cx);
3955 let all_selections_were_entire_line =
3956 clipboard_selections.iter().all(|s| s.is_entire_line);
3957 let first_selection_indent_column =
3958 clipboard_selections.first().map(|s| s.first_line_indent);
3959 if clipboard_selections.len() != old_selections.len() {
3960 let mut newline_separated_text = String::new();
3961 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3962 let mut ix = 0;
3963 while let Some(clipboard_selection) = clipboard_selections.next() {
3964 newline_separated_text
3965 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3966 ix += clipboard_selection.len;
3967 if clipboard_selections.peek().is_some() {
3968 newline_separated_text.push('\n');
3969 }
3970 }
3971 clipboard_text = Cow::Owned(newline_separated_text);
3972 }
3973
3974 this.buffer.update(cx, |buffer, cx| {
3975 let snapshot = buffer.read(cx);
3976 let mut start_offset = 0;
3977 let mut edits = Vec::new();
3978 let mut original_indent_columns = Vec::new();
3979 let line_mode = this.selections.line_mode;
3980 for (ix, selection) in old_selections.iter().enumerate() {
3981 let to_insert;
3982 let entire_line;
3983 let original_indent_column;
3984 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3985 let end_offset = start_offset + clipboard_selection.len;
3986 to_insert = &clipboard_text[start_offset..end_offset];
3987 entire_line = clipboard_selection.is_entire_line;
3988 start_offset = end_offset;
3989 original_indent_column =
3990 Some(clipboard_selection.first_line_indent);
3991 } else {
3992 to_insert = clipboard_text.as_str();
3993 entire_line = all_selections_were_entire_line;
3994 original_indent_column = first_selection_indent_column
3995 }
3996
3997 // If the corresponding selection was empty when this slice of the
3998 // clipboard text was written, then the entire line containing the
3999 // selection was copied. If this selection is also currently empty,
4000 // then paste the line before the current line of the buffer.
4001 let range = if selection.is_empty() && !line_mode && entire_line {
4002 let column = selection.start.to_point(&snapshot).column as usize;
4003 let line_start = selection.start - column;
4004 line_start..line_start
4005 } else {
4006 selection.range()
4007 };
4008
4009 edits.push((range, to_insert));
4010 original_indent_columns.extend(original_indent_column);
4011 }
4012 drop(snapshot);
4013
4014 buffer.edit(
4015 edits,
4016 Some(AutoindentMode::Block {
4017 original_indent_columns,
4018 }),
4019 cx,
4020 );
4021 });
4022
4023 let selections = this.selections.all::<usize>(cx);
4024 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4025 } else {
4026 this.insert(&clipboard_text, cx);
4027 }
4028 }
4029 });
4030 }
4031
4032 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4033 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4034 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4035 self.change_selections(None, cx, |s| {
4036 s.select_anchors(selections.to_vec());
4037 });
4038 }
4039 self.request_autoscroll(Autoscroll::fit(), cx);
4040 self.unmark_text(cx);
4041 cx.emit(Event::Edited);
4042 }
4043 }
4044
4045 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4046 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4047 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4048 {
4049 self.change_selections(None, cx, |s| {
4050 s.select_anchors(selections.to_vec());
4051 });
4052 }
4053 self.request_autoscroll(Autoscroll::fit(), cx);
4054 self.unmark_text(cx);
4055 cx.emit(Event::Edited);
4056 }
4057 }
4058
4059 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4060 self.buffer
4061 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4062 }
4063
4064 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4065 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4066 let line_mode = s.line_mode;
4067 s.move_with(|map, selection| {
4068 let cursor = if selection.is_empty() && !line_mode {
4069 movement::left(map, selection.start)
4070 } else {
4071 selection.start
4072 };
4073 selection.collapse_to(cursor, SelectionGoal::None);
4074 });
4075 })
4076 }
4077
4078 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4079 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4080 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4081 })
4082 }
4083
4084 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4085 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4086 let line_mode = s.line_mode;
4087 s.move_with(|map, selection| {
4088 let cursor = if selection.is_empty() && !line_mode {
4089 movement::right(map, selection.end)
4090 } else {
4091 selection.end
4092 };
4093 selection.collapse_to(cursor, SelectionGoal::None)
4094 });
4095 })
4096 }
4097
4098 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4099 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4100 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4101 })
4102 }
4103
4104 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4105 if self.take_rename(true, cx).is_some() {
4106 return;
4107 }
4108
4109 if let Some(context_menu) = self.context_menu.as_mut() {
4110 if context_menu.select_prev(cx) {
4111 return;
4112 }
4113 }
4114
4115 if matches!(self.mode, EditorMode::SingleLine) {
4116 cx.propagate_action();
4117 return;
4118 }
4119
4120 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4121 let line_mode = s.line_mode;
4122 s.move_with(|map, selection| {
4123 if !selection.is_empty() && !line_mode {
4124 selection.goal = SelectionGoal::None;
4125 }
4126 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4127 selection.collapse_to(cursor, goal);
4128 });
4129 })
4130 }
4131
4132 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4133 if self.take_rename(true, cx).is_some() {
4134 return;
4135 }
4136
4137 if self
4138 .context_menu
4139 .as_mut()
4140 .map(|menu| menu.select_first(cx))
4141 .unwrap_or(false)
4142 {
4143 return;
4144 }
4145
4146 if matches!(self.mode, EditorMode::SingleLine) {
4147 cx.propagate_action();
4148 return;
4149 }
4150
4151 let row_count = if let Some(row_count) = self.visible_line_count() {
4152 row_count as u32 - 1
4153 } else {
4154 return;
4155 };
4156
4157 let autoscroll = if action.center_cursor {
4158 Autoscroll::center()
4159 } else {
4160 Autoscroll::fit()
4161 };
4162
4163 self.change_selections(Some(autoscroll), cx, |s| {
4164 let line_mode = s.line_mode;
4165 s.move_with(|map, selection| {
4166 if !selection.is_empty() && !line_mode {
4167 selection.goal = SelectionGoal::None;
4168 }
4169 let (cursor, goal) =
4170 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4171 selection.collapse_to(cursor, goal);
4172 });
4173 });
4174 }
4175
4176 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4177 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4178 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4179 })
4180 }
4181
4182 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4183 self.take_rename(true, cx);
4184
4185 if let Some(context_menu) = self.context_menu.as_mut() {
4186 if context_menu.select_next(cx) {
4187 return;
4188 }
4189 }
4190
4191 if self.mode == EditorMode::SingleLine {
4192 cx.propagate_action();
4193 return;
4194 }
4195
4196 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4197 let line_mode = s.line_mode;
4198 s.move_with(|map, selection| {
4199 if !selection.is_empty() && !line_mode {
4200 selection.goal = SelectionGoal::None;
4201 }
4202 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4203 selection.collapse_to(cursor, goal);
4204 });
4205 });
4206 }
4207
4208 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4209 if self.take_rename(true, cx).is_some() {
4210 return;
4211 }
4212
4213 if self
4214 .context_menu
4215 .as_mut()
4216 .map(|menu| menu.select_last(cx))
4217 .unwrap_or(false)
4218 {
4219 return;
4220 }
4221
4222 if matches!(self.mode, EditorMode::SingleLine) {
4223 cx.propagate_action();
4224 return;
4225 }
4226
4227 let row_count = if let Some(row_count) = self.visible_line_count() {
4228 row_count as u32 - 1
4229 } else {
4230 return;
4231 };
4232
4233 let autoscroll = if action.center_cursor {
4234 Autoscroll::center()
4235 } else {
4236 Autoscroll::fit()
4237 };
4238
4239 self.change_selections(Some(autoscroll), cx, |s| {
4240 let line_mode = s.line_mode;
4241 s.move_with(|map, selection| {
4242 if !selection.is_empty() && !line_mode {
4243 selection.goal = SelectionGoal::None;
4244 }
4245 let (cursor, goal) =
4246 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4247 selection.collapse_to(cursor, goal);
4248 });
4249 });
4250 }
4251
4252 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4253 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4254 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4255 });
4256 }
4257
4258 pub fn move_to_previous_word_start(
4259 &mut self,
4260 _: &MoveToPreviousWordStart,
4261 cx: &mut ViewContext<Self>,
4262 ) {
4263 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4264 s.move_cursors_with(|map, head, _| {
4265 (
4266 movement::previous_word_start(map, head),
4267 SelectionGoal::None,
4268 )
4269 });
4270 })
4271 }
4272
4273 pub fn move_to_previous_subword_start(
4274 &mut self,
4275 _: &MoveToPreviousSubwordStart,
4276 cx: &mut ViewContext<Self>,
4277 ) {
4278 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4279 s.move_cursors_with(|map, head, _| {
4280 (
4281 movement::previous_subword_start(map, head),
4282 SelectionGoal::None,
4283 )
4284 });
4285 })
4286 }
4287
4288 pub fn select_to_previous_word_start(
4289 &mut self,
4290 _: &SelectToPreviousWordStart,
4291 cx: &mut ViewContext<Self>,
4292 ) {
4293 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4294 s.move_heads_with(|map, head, _| {
4295 (
4296 movement::previous_word_start(map, head),
4297 SelectionGoal::None,
4298 )
4299 });
4300 })
4301 }
4302
4303 pub fn select_to_previous_subword_start(
4304 &mut self,
4305 _: &SelectToPreviousSubwordStart,
4306 cx: &mut ViewContext<Self>,
4307 ) {
4308 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4309 s.move_heads_with(|map, head, _| {
4310 (
4311 movement::previous_subword_start(map, head),
4312 SelectionGoal::None,
4313 )
4314 });
4315 })
4316 }
4317
4318 pub fn delete_to_previous_word_start(
4319 &mut self,
4320 _: &DeleteToPreviousWordStart,
4321 cx: &mut ViewContext<Self>,
4322 ) {
4323 self.transact(cx, |this, cx| {
4324 this.select_autoclose_pair(cx);
4325 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4326 let line_mode = s.line_mode;
4327 s.move_with(|map, selection| {
4328 if selection.is_empty() && !line_mode {
4329 let cursor = movement::previous_word_start(map, selection.head());
4330 selection.set_head(cursor, SelectionGoal::None);
4331 }
4332 });
4333 });
4334 this.insert("", cx);
4335 });
4336 }
4337
4338 pub fn delete_to_previous_subword_start(
4339 &mut self,
4340 _: &DeleteToPreviousSubwordStart,
4341 cx: &mut ViewContext<Self>,
4342 ) {
4343 self.transact(cx, |this, cx| {
4344 this.select_autoclose_pair(cx);
4345 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4346 let line_mode = s.line_mode;
4347 s.move_with(|map, selection| {
4348 if selection.is_empty() && !line_mode {
4349 let cursor = movement::previous_subword_start(map, selection.head());
4350 selection.set_head(cursor, SelectionGoal::None);
4351 }
4352 });
4353 });
4354 this.insert("", cx);
4355 });
4356 }
4357
4358 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4359 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4360 s.move_cursors_with(|map, head, _| {
4361 (movement::next_word_end(map, head), SelectionGoal::None)
4362 });
4363 })
4364 }
4365
4366 pub fn move_to_next_subword_end(
4367 &mut self,
4368 _: &MoveToNextSubwordEnd,
4369 cx: &mut ViewContext<Self>,
4370 ) {
4371 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4372 s.move_cursors_with(|map, head, _| {
4373 (movement::next_subword_end(map, head), SelectionGoal::None)
4374 });
4375 })
4376 }
4377
4378 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4379 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4380 s.move_heads_with(|map, head, _| {
4381 (movement::next_word_end(map, head), SelectionGoal::None)
4382 });
4383 })
4384 }
4385
4386 pub fn select_to_next_subword_end(
4387 &mut self,
4388 _: &SelectToNextSubwordEnd,
4389 cx: &mut ViewContext<Self>,
4390 ) {
4391 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4392 s.move_heads_with(|map, head, _| {
4393 (movement::next_subword_end(map, head), SelectionGoal::None)
4394 });
4395 })
4396 }
4397
4398 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4399 self.transact(cx, |this, cx| {
4400 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4401 let line_mode = s.line_mode;
4402 s.move_with(|map, selection| {
4403 if selection.is_empty() && !line_mode {
4404 let cursor = movement::next_word_end(map, selection.head());
4405 selection.set_head(cursor, SelectionGoal::None);
4406 }
4407 });
4408 });
4409 this.insert("", cx);
4410 });
4411 }
4412
4413 pub fn delete_to_next_subword_end(
4414 &mut self,
4415 _: &DeleteToNextSubwordEnd,
4416 cx: &mut ViewContext<Self>,
4417 ) {
4418 self.transact(cx, |this, cx| {
4419 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4420 s.move_with(|map, selection| {
4421 if selection.is_empty() {
4422 let cursor = movement::next_subword_end(map, selection.head());
4423 selection.set_head(cursor, SelectionGoal::None);
4424 }
4425 });
4426 });
4427 this.insert("", cx);
4428 });
4429 }
4430
4431 pub fn move_to_beginning_of_line(
4432 &mut self,
4433 _: &MoveToBeginningOfLine,
4434 cx: &mut ViewContext<Self>,
4435 ) {
4436 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4437 s.move_cursors_with(|map, head, _| {
4438 (
4439 movement::indented_line_beginning(map, head, true),
4440 SelectionGoal::None,
4441 )
4442 });
4443 })
4444 }
4445
4446 pub fn select_to_beginning_of_line(
4447 &mut self,
4448 action: &SelectToBeginningOfLine,
4449 cx: &mut ViewContext<Self>,
4450 ) {
4451 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4452 s.move_heads_with(|map, head, _| {
4453 (
4454 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4455 SelectionGoal::None,
4456 )
4457 });
4458 });
4459 }
4460
4461 pub fn delete_to_beginning_of_line(
4462 &mut self,
4463 _: &DeleteToBeginningOfLine,
4464 cx: &mut ViewContext<Self>,
4465 ) {
4466 self.transact(cx, |this, cx| {
4467 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4468 s.move_with(|_, selection| {
4469 selection.reversed = true;
4470 });
4471 });
4472
4473 this.select_to_beginning_of_line(
4474 &SelectToBeginningOfLine {
4475 stop_at_soft_wraps: false,
4476 },
4477 cx,
4478 );
4479 this.backspace(&Backspace, cx);
4480 });
4481 }
4482
4483 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4484 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4485 s.move_cursors_with(|map, head, _| {
4486 (movement::line_end(map, head, true), SelectionGoal::None)
4487 });
4488 })
4489 }
4490
4491 pub fn select_to_end_of_line(
4492 &mut self,
4493 action: &SelectToEndOfLine,
4494 cx: &mut ViewContext<Self>,
4495 ) {
4496 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4497 s.move_heads_with(|map, head, _| {
4498 (
4499 movement::line_end(map, head, action.stop_at_soft_wraps),
4500 SelectionGoal::None,
4501 )
4502 });
4503 })
4504 }
4505
4506 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4507 self.transact(cx, |this, cx| {
4508 this.select_to_end_of_line(
4509 &SelectToEndOfLine {
4510 stop_at_soft_wraps: false,
4511 },
4512 cx,
4513 );
4514 this.delete(&Delete, cx);
4515 });
4516 }
4517
4518 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4519 self.transact(cx, |this, cx| {
4520 this.select_to_end_of_line(
4521 &SelectToEndOfLine {
4522 stop_at_soft_wraps: false,
4523 },
4524 cx,
4525 );
4526 this.cut(&Cut, cx);
4527 });
4528 }
4529
4530 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4531 if matches!(self.mode, EditorMode::SingleLine) {
4532 cx.propagate_action();
4533 return;
4534 }
4535
4536 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4537 s.select_ranges(vec![0..0]);
4538 });
4539 }
4540
4541 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4542 let mut selection = self.selections.last::<Point>(cx);
4543 selection.set_head(Point::zero(), SelectionGoal::None);
4544
4545 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4546 s.select(vec![selection]);
4547 });
4548 }
4549
4550 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4551 if matches!(self.mode, EditorMode::SingleLine) {
4552 cx.propagate_action();
4553 return;
4554 }
4555
4556 let cursor = self.buffer.read(cx).read(cx).len();
4557 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4558 s.select_ranges(vec![cursor..cursor])
4559 });
4560 }
4561
4562 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4563 self.nav_history = nav_history;
4564 }
4565
4566 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4567 self.nav_history.as_ref()
4568 }
4569
4570 fn push_to_nav_history(
4571 &self,
4572 cursor_anchor: Anchor,
4573 new_position: Option<Point>,
4574 cx: &mut ViewContext<Self>,
4575 ) {
4576 if let Some(nav_history) = &self.nav_history {
4577 let buffer = self.buffer.read(cx).read(cx);
4578 let cursor_position = cursor_anchor.to_point(&buffer);
4579 let scroll_state = self.scroll_manager.anchor();
4580 let scroll_top_row = scroll_state.top_row(&buffer);
4581 drop(buffer);
4582
4583 if let Some(new_position) = new_position {
4584 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
4585 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4586 return;
4587 }
4588 }
4589
4590 nav_history.push(
4591 Some(NavigationData {
4592 cursor_anchor,
4593 cursor_position,
4594 scroll_anchor: scroll_state,
4595 scroll_top_row,
4596 }),
4597 cx,
4598 );
4599 }
4600 }
4601
4602 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4603 let buffer = self.buffer.read(cx).snapshot(cx);
4604 let mut selection = self.selections.first::<usize>(cx);
4605 selection.set_head(buffer.len(), SelectionGoal::None);
4606 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4607 s.select(vec![selection]);
4608 });
4609 }
4610
4611 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4612 let end = self.buffer.read(cx).read(cx).len();
4613 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4614 s.select_ranges(vec![0..end]);
4615 });
4616 }
4617
4618 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4619 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4620 let mut selections = self.selections.all::<Point>(cx);
4621 let max_point = display_map.buffer_snapshot.max_point();
4622 for selection in &mut selections {
4623 let rows = selection.spanned_rows(true, &display_map);
4624 selection.start = Point::new(rows.start, 0);
4625 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4626 selection.reversed = false;
4627 }
4628 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4629 s.select(selections);
4630 });
4631 }
4632
4633 pub fn split_selection_into_lines(
4634 &mut self,
4635 _: &SplitSelectionIntoLines,
4636 cx: &mut ViewContext<Self>,
4637 ) {
4638 let mut to_unfold = Vec::new();
4639 let mut new_selection_ranges = Vec::new();
4640 {
4641 let selections = self.selections.all::<Point>(cx);
4642 let buffer = self.buffer.read(cx).read(cx);
4643 for selection in selections {
4644 for row in selection.start.row..selection.end.row {
4645 let cursor = Point::new(row, buffer.line_len(row));
4646 new_selection_ranges.push(cursor..cursor);
4647 }
4648 new_selection_ranges.push(selection.end..selection.end);
4649 to_unfold.push(selection.start..selection.end);
4650 }
4651 }
4652 self.unfold_ranges(to_unfold, true, true, cx);
4653 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4654 s.select_ranges(new_selection_ranges);
4655 });
4656 }
4657
4658 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4659 self.add_selection(true, cx);
4660 }
4661
4662 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4663 self.add_selection(false, cx);
4664 }
4665
4666 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4667 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4668 let mut selections = self.selections.all::<Point>(cx);
4669 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4670 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4671 let range = oldest_selection.display_range(&display_map).sorted();
4672 let columns = cmp::min(range.start.column(), range.end.column())
4673 ..cmp::max(range.start.column(), range.end.column());
4674
4675 selections.clear();
4676 let mut stack = Vec::new();
4677 for row in range.start.row()..=range.end.row() {
4678 if let Some(selection) = self.selections.build_columnar_selection(
4679 &display_map,
4680 row,
4681 &columns,
4682 oldest_selection.reversed,
4683 ) {
4684 stack.push(selection.id);
4685 selections.push(selection);
4686 }
4687 }
4688
4689 if above {
4690 stack.reverse();
4691 }
4692
4693 AddSelectionsState { above, stack }
4694 });
4695
4696 let last_added_selection = *state.stack.last().unwrap();
4697 let mut new_selections = Vec::new();
4698 if above == state.above {
4699 let end_row = if above {
4700 0
4701 } else {
4702 display_map.max_point().row()
4703 };
4704
4705 'outer: for selection in selections {
4706 if selection.id == last_added_selection {
4707 let range = selection.display_range(&display_map).sorted();
4708 debug_assert_eq!(range.start.row(), range.end.row());
4709 let mut row = range.start.row();
4710 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4711 {
4712 start..end
4713 } else {
4714 cmp::min(range.start.column(), range.end.column())
4715 ..cmp::max(range.start.column(), range.end.column())
4716 };
4717
4718 while row != end_row {
4719 if above {
4720 row -= 1;
4721 } else {
4722 row += 1;
4723 }
4724
4725 if let Some(new_selection) = self.selections.build_columnar_selection(
4726 &display_map,
4727 row,
4728 &columns,
4729 selection.reversed,
4730 ) {
4731 state.stack.push(new_selection.id);
4732 if above {
4733 new_selections.push(new_selection);
4734 new_selections.push(selection);
4735 } else {
4736 new_selections.push(selection);
4737 new_selections.push(new_selection);
4738 }
4739
4740 continue 'outer;
4741 }
4742 }
4743 }
4744
4745 new_selections.push(selection);
4746 }
4747 } else {
4748 new_selections = selections;
4749 new_selections.retain(|s| s.id != last_added_selection);
4750 state.stack.pop();
4751 }
4752
4753 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4754 s.select(new_selections);
4755 });
4756 if state.stack.len() > 1 {
4757 self.add_selections_state = Some(state);
4758 }
4759 }
4760
4761 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4762 self.push_to_selection_history();
4763 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4764 let buffer = &display_map.buffer_snapshot;
4765 let mut selections = self.selections.all::<usize>(cx);
4766 if let Some(mut select_next_state) = self.select_next_state.take() {
4767 let query = &select_next_state.query;
4768 if !select_next_state.done {
4769 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4770 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4771 let mut next_selected_range = None;
4772
4773 let bytes_after_last_selection =
4774 buffer.bytes_in_range(last_selection.end..buffer.len());
4775 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4776 let query_matches = query
4777 .stream_find_iter(bytes_after_last_selection)
4778 .map(|result| (last_selection.end, result))
4779 .chain(
4780 query
4781 .stream_find_iter(bytes_before_first_selection)
4782 .map(|result| (0, result)),
4783 );
4784 for (start_offset, query_match) in query_matches {
4785 let query_match = query_match.unwrap(); // can only fail due to I/O
4786 let offset_range =
4787 start_offset + query_match.start()..start_offset + query_match.end();
4788 let display_range = offset_range.start.to_display_point(&display_map)
4789 ..offset_range.end.to_display_point(&display_map);
4790
4791 if !select_next_state.wordwise
4792 || (!movement::is_inside_word(&display_map, display_range.start)
4793 && !movement::is_inside_word(&display_map, display_range.end))
4794 {
4795 next_selected_range = Some(offset_range);
4796 break;
4797 }
4798 }
4799
4800 if let Some(next_selected_range) = next_selected_range {
4801 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
4802 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4803 if action.replace_newest {
4804 s.delete(s.newest_anchor().id);
4805 }
4806 s.insert_range(next_selected_range);
4807 });
4808 } else {
4809 select_next_state.done = true;
4810 }
4811 }
4812
4813 self.select_next_state = Some(select_next_state);
4814 } else if selections.len() == 1 {
4815 let selection = selections.last_mut().unwrap();
4816 if selection.start == selection.end {
4817 let word_range = movement::surrounding_word(
4818 &display_map,
4819 selection.start.to_display_point(&display_map),
4820 );
4821 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4822 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4823 selection.goal = SelectionGoal::None;
4824 selection.reversed = false;
4825
4826 let query = buffer
4827 .text_for_range(selection.start..selection.end)
4828 .collect::<String>();
4829 let select_state = SelectNextState {
4830 query: AhoCorasick::new_auto_configured(&[query]),
4831 wordwise: true,
4832 done: false,
4833 };
4834 self.unfold_ranges([selection.start..selection.end], false, true, cx);
4835 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4836 s.select(selections);
4837 });
4838 self.select_next_state = Some(select_state);
4839 } else {
4840 let query = buffer
4841 .text_for_range(selection.start..selection.end)
4842 .collect::<String>();
4843 self.select_next_state = Some(SelectNextState {
4844 query: AhoCorasick::new_auto_configured(&[query]),
4845 wordwise: false,
4846 done: false,
4847 });
4848 self.select_next(action, cx);
4849 }
4850 }
4851 }
4852
4853 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
4854 self.transact(cx, |this, cx| {
4855 let mut selections = this.selections.all::<Point>(cx);
4856 let mut edits = Vec::new();
4857 let mut selection_edit_ranges = Vec::new();
4858 let mut last_toggled_row = None;
4859 let snapshot = this.buffer.read(cx).read(cx);
4860 let empty_str: Arc<str> = "".into();
4861 let mut suffixes_inserted = Vec::new();
4862
4863 fn comment_prefix_range(
4864 snapshot: &MultiBufferSnapshot,
4865 row: u32,
4866 comment_prefix: &str,
4867 comment_prefix_whitespace: &str,
4868 ) -> Range<Point> {
4869 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4870
4871 let mut line_bytes = snapshot
4872 .bytes_in_range(start..snapshot.max_point())
4873 .flatten()
4874 .copied();
4875
4876 // If this line currently begins with the line comment prefix, then record
4877 // the range containing the prefix.
4878 if line_bytes
4879 .by_ref()
4880 .take(comment_prefix.len())
4881 .eq(comment_prefix.bytes())
4882 {
4883 // Include any whitespace that matches the comment prefix.
4884 let matching_whitespace_len = line_bytes
4885 .zip(comment_prefix_whitespace.bytes())
4886 .take_while(|(a, b)| a == b)
4887 .count() as u32;
4888 let end = Point::new(
4889 start.row,
4890 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
4891 );
4892 start..end
4893 } else {
4894 start..start
4895 }
4896 }
4897
4898 fn comment_suffix_range(
4899 snapshot: &MultiBufferSnapshot,
4900 row: u32,
4901 comment_suffix: &str,
4902 comment_suffix_has_leading_space: bool,
4903 ) -> Range<Point> {
4904 let end = Point::new(row, snapshot.line_len(row));
4905 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
4906
4907 let mut line_end_bytes = snapshot
4908 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
4909 .flatten()
4910 .copied();
4911
4912 let leading_space_len = if suffix_start_column > 0
4913 && line_end_bytes.next() == Some(b' ')
4914 && comment_suffix_has_leading_space
4915 {
4916 1
4917 } else {
4918 0
4919 };
4920
4921 // If this line currently begins with the line comment prefix, then record
4922 // the range containing the prefix.
4923 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
4924 let start = Point::new(end.row, suffix_start_column - leading_space_len);
4925 start..end
4926 } else {
4927 end..end
4928 }
4929 }
4930
4931 // TODO: Handle selections that cross excerpts
4932 for selection in &mut selections {
4933 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
4934 let language = if let Some(language) =
4935 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
4936 {
4937 language
4938 } else {
4939 continue;
4940 };
4941
4942 selection_edit_ranges.clear();
4943
4944 // If multiple selections contain a given row, avoid processing that
4945 // row more than once.
4946 let mut start_row = selection.start.row;
4947 if last_toggled_row == Some(start_row) {
4948 start_row += 1;
4949 }
4950 let end_row =
4951 if selection.end.row > selection.start.row && selection.end.column == 0 {
4952 selection.end.row - 1
4953 } else {
4954 selection.end.row
4955 };
4956 last_toggled_row = Some(end_row);
4957
4958 if start_row > end_row {
4959 continue;
4960 }
4961
4962 // If the language has line comments, toggle those.
4963 if let Some(full_comment_prefix) = language.line_comment_prefix() {
4964 // Split the comment prefix's trailing whitespace into a separate string,
4965 // as that portion won't be used for detecting if a line is a comment.
4966 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4967 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4968 let mut all_selection_lines_are_comments = true;
4969
4970 for row in start_row..=end_row {
4971 if snapshot.is_line_blank(row) {
4972 continue;
4973 }
4974
4975 let prefix_range = comment_prefix_range(
4976 snapshot.deref(),
4977 row,
4978 comment_prefix,
4979 comment_prefix_whitespace,
4980 );
4981 if prefix_range.is_empty() {
4982 all_selection_lines_are_comments = false;
4983 }
4984 selection_edit_ranges.push(prefix_range);
4985 }
4986
4987 if all_selection_lines_are_comments {
4988 edits.extend(
4989 selection_edit_ranges
4990 .iter()
4991 .cloned()
4992 .map(|range| (range, empty_str.clone())),
4993 );
4994 } else {
4995 let min_column = selection_edit_ranges
4996 .iter()
4997 .map(|r| r.start.column)
4998 .min()
4999 .unwrap_or(0);
5000 edits.extend(selection_edit_ranges.iter().map(|range| {
5001 let position = Point::new(range.start.row, min_column);
5002 (position..position, full_comment_prefix.clone())
5003 }));
5004 }
5005 } else if let Some((full_comment_prefix, comment_suffix)) =
5006 language.block_comment_delimiters()
5007 {
5008 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5009 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5010 let prefix_range = comment_prefix_range(
5011 snapshot.deref(),
5012 start_row,
5013 comment_prefix,
5014 comment_prefix_whitespace,
5015 );
5016 let suffix_range = comment_suffix_range(
5017 snapshot.deref(),
5018 end_row,
5019 comment_suffix.trim_start_matches(' '),
5020 comment_suffix.starts_with(' '),
5021 );
5022
5023 if prefix_range.is_empty() || suffix_range.is_empty() {
5024 edits.push((
5025 prefix_range.start..prefix_range.start,
5026 full_comment_prefix.clone(),
5027 ));
5028 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5029 suffixes_inserted.push((end_row, comment_suffix.len()));
5030 } else {
5031 edits.push((prefix_range, empty_str.clone()));
5032 edits.push((suffix_range, empty_str.clone()));
5033 }
5034 } else {
5035 continue;
5036 }
5037 }
5038
5039 drop(snapshot);
5040 this.buffer.update(cx, |buffer, cx| {
5041 buffer.edit(edits, None, cx);
5042 });
5043
5044 // Adjust selections so that they end before any comment suffixes that
5045 // were inserted.
5046 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5047 let mut selections = this.selections.all::<Point>(cx);
5048 let snapshot = this.buffer.read(cx).read(cx);
5049 for selection in &mut selections {
5050 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5051 match row.cmp(&selection.end.row) {
5052 Ordering::Less => {
5053 suffixes_inserted.next();
5054 continue;
5055 }
5056 Ordering::Greater => break,
5057 Ordering::Equal => {
5058 if selection.end.column == snapshot.line_len(row) {
5059 if selection.is_empty() {
5060 selection.start.column -= suffix_len as u32;
5061 }
5062 selection.end.column -= suffix_len as u32;
5063 }
5064 break;
5065 }
5066 }
5067 }
5068 }
5069
5070 drop(snapshot);
5071 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5072
5073 let selections = this.selections.all::<Point>(cx);
5074 let selections_on_single_row = selections.windows(2).all(|selections| {
5075 selections[0].start.row == selections[1].start.row
5076 && selections[0].end.row == selections[1].end.row
5077 && selections[0].start.row == selections[0].end.row
5078 });
5079 let selections_selecting = selections
5080 .iter()
5081 .any(|selection| selection.start != selection.end);
5082 let advance_downwards = action.advance_downwards
5083 && selections_on_single_row
5084 && !selections_selecting
5085 && this.mode != EditorMode::SingleLine;
5086
5087 if advance_downwards {
5088 let snapshot = this.buffer.read(cx).snapshot(cx);
5089
5090 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5091 s.move_cursors_with(|display_snapshot, display_point, _| {
5092 let mut point = display_point.to_point(display_snapshot);
5093 point.row += 1;
5094 point = snapshot.clip_point(point, Bias::Left);
5095 let display_point = point.to_display_point(display_snapshot);
5096 (display_point, SelectionGoal::Column(display_point.column()))
5097 })
5098 });
5099 }
5100 });
5101 }
5102
5103 pub fn select_larger_syntax_node(
5104 &mut self,
5105 _: &SelectLargerSyntaxNode,
5106 cx: &mut ViewContext<Self>,
5107 ) {
5108 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5109 let buffer = self.buffer.read(cx).snapshot(cx);
5110 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5111
5112 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5113 let mut selected_larger_node = false;
5114 let new_selections = old_selections
5115 .iter()
5116 .map(|selection| {
5117 let old_range = selection.start..selection.end;
5118 let mut new_range = old_range.clone();
5119 while let Some(containing_range) =
5120 buffer.range_for_syntax_ancestor(new_range.clone())
5121 {
5122 new_range = containing_range;
5123 if !display_map.intersects_fold(new_range.start)
5124 && !display_map.intersects_fold(new_range.end)
5125 {
5126 break;
5127 }
5128 }
5129
5130 selected_larger_node |= new_range != old_range;
5131 Selection {
5132 id: selection.id,
5133 start: new_range.start,
5134 end: new_range.end,
5135 goal: SelectionGoal::None,
5136 reversed: selection.reversed,
5137 }
5138 })
5139 .collect::<Vec<_>>();
5140
5141 if selected_larger_node {
5142 stack.push(old_selections);
5143 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5144 s.select(new_selections);
5145 });
5146 }
5147 self.select_larger_syntax_node_stack = stack;
5148 }
5149
5150 pub fn select_smaller_syntax_node(
5151 &mut self,
5152 _: &SelectSmallerSyntaxNode,
5153 cx: &mut ViewContext<Self>,
5154 ) {
5155 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5156 if let Some(selections) = stack.pop() {
5157 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5158 s.select(selections.to_vec());
5159 });
5160 }
5161 self.select_larger_syntax_node_stack = stack;
5162 }
5163
5164 pub fn move_to_enclosing_bracket(
5165 &mut self,
5166 _: &MoveToEnclosingBracket,
5167 cx: &mut ViewContext<Self>,
5168 ) {
5169 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5170 s.move_offsets_with(|snapshot, selection| {
5171 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5172 return;
5173 };
5174
5175 let mut best_length = usize::MAX;
5176 let mut best_inside = false;
5177 let mut best_in_bracket_range = false;
5178 let mut best_destination = None;
5179 for (open, close) in enclosing_bracket_ranges {
5180 let close = close.to_inclusive();
5181 let length = close.end() - open.start;
5182 let inside = selection.start >= open.end && selection.end <= *close.start();
5183 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5184
5185 // If best is next to a bracket and current isn't, skip
5186 if !in_bracket_range && best_in_bracket_range {
5187 continue;
5188 }
5189
5190 // Prefer smaller lengths unless best is inside and current isn't
5191 if length > best_length && (best_inside || !inside) {
5192 continue;
5193 }
5194
5195 best_length = length;
5196 best_inside = inside;
5197 best_in_bracket_range = in_bracket_range;
5198 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5199 if inside {
5200 open.end
5201 } else {
5202 open.start
5203 }
5204 } else {
5205 if inside {
5206 *close.start()
5207 } else {
5208 *close.end()
5209 }
5210 });
5211 }
5212
5213 if let Some(destination) = best_destination {
5214 selection.collapse_to(destination, SelectionGoal::None);
5215 }
5216 })
5217 });
5218 }
5219
5220 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5221 self.end_selection(cx);
5222 self.selection_history.mode = SelectionHistoryMode::Undoing;
5223 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5224 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5225 self.select_next_state = entry.select_next_state;
5226 self.add_selections_state = entry.add_selections_state;
5227 self.request_autoscroll(Autoscroll::newest(), cx);
5228 }
5229 self.selection_history.mode = SelectionHistoryMode::Normal;
5230 }
5231
5232 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5233 self.end_selection(cx);
5234 self.selection_history.mode = SelectionHistoryMode::Redoing;
5235 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5236 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5237 self.select_next_state = entry.select_next_state;
5238 self.add_selections_state = entry.add_selections_state;
5239 self.request_autoscroll(Autoscroll::newest(), cx);
5240 }
5241 self.selection_history.mode = SelectionHistoryMode::Normal;
5242 }
5243
5244 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5245 self.go_to_diagnostic_impl(Direction::Next, cx)
5246 }
5247
5248 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5249 self.go_to_diagnostic_impl(Direction::Prev, cx)
5250 }
5251
5252 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5253 let buffer = self.buffer.read(cx).snapshot(cx);
5254 let selection = self.selections.newest::<usize>(cx);
5255
5256 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5257 if direction == Direction::Next {
5258 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5259 let (group_id, jump_to) = popover.activation_info();
5260 if self.activate_diagnostics(group_id, cx) {
5261 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5262 let mut new_selection = s.newest_anchor().clone();
5263 new_selection.collapse_to(jump_to, SelectionGoal::None);
5264 s.select_anchors(vec![new_selection.clone()]);
5265 });
5266 }
5267 return;
5268 }
5269 }
5270
5271 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5272 active_diagnostics
5273 .primary_range
5274 .to_offset(&buffer)
5275 .to_inclusive()
5276 });
5277 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5278 if active_primary_range.contains(&selection.head()) {
5279 *active_primary_range.end()
5280 } else {
5281 selection.head()
5282 }
5283 } else {
5284 selection.head()
5285 };
5286
5287 loop {
5288 let mut diagnostics = if direction == Direction::Prev {
5289 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5290 } else {
5291 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5292 };
5293 let group = diagnostics.find_map(|entry| {
5294 if entry.diagnostic.is_primary
5295 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5296 && !entry.range.is_empty()
5297 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5298 {
5299 Some((entry.range, entry.diagnostic.group_id))
5300 } else {
5301 None
5302 }
5303 });
5304
5305 if let Some((primary_range, group_id)) = group {
5306 if self.activate_diagnostics(group_id, cx) {
5307 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5308 s.select(vec![Selection {
5309 id: selection.id,
5310 start: primary_range.start,
5311 end: primary_range.start,
5312 reversed: false,
5313 goal: SelectionGoal::None,
5314 }]);
5315 });
5316 }
5317 break;
5318 } else {
5319 // Cycle around to the start of the buffer, potentially moving back to the start of
5320 // the currently active diagnostic.
5321 active_primary_range.take();
5322 if direction == Direction::Prev {
5323 if search_start == buffer.len() {
5324 break;
5325 } else {
5326 search_start = buffer.len();
5327 }
5328 } else if search_start == 0 {
5329 break;
5330 } else {
5331 search_start = 0;
5332 }
5333 }
5334 }
5335 }
5336
5337 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5338 self.go_to_hunk_impl(Direction::Next, cx)
5339 }
5340
5341 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5342 self.go_to_hunk_impl(Direction::Prev, cx)
5343 }
5344
5345 pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5346 let snapshot = self
5347 .display_map
5348 .update(cx, |display_map, cx| display_map.snapshot(cx));
5349 let selection = self.selections.newest::<Point>(cx);
5350
5351 fn seek_in_direction(
5352 this: &mut Editor,
5353 snapshot: &DisplaySnapshot,
5354 initial_point: Point,
5355 is_wrapped: bool,
5356 direction: Direction,
5357 cx: &mut ViewContext<Editor>,
5358 ) -> bool {
5359 let hunks = if direction == Direction::Next {
5360 snapshot
5361 .buffer_snapshot
5362 .git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
5363 } else {
5364 snapshot
5365 .buffer_snapshot
5366 .git_diff_hunks_in_range(0..initial_point.row, true)
5367 };
5368
5369 let display_point = initial_point.to_display_point(snapshot);
5370 let mut hunks = hunks
5371 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5372 .skip_while(|hunk| {
5373 if is_wrapped {
5374 false
5375 } else {
5376 hunk.contains_display_row(display_point.row())
5377 }
5378 })
5379 .dedup();
5380
5381 if let Some(hunk) = hunks.next() {
5382 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5383 let row = hunk.start_display_row();
5384 let point = DisplayPoint::new(row, 0);
5385 s.select_display_ranges([point..point]);
5386 });
5387
5388 true
5389 } else {
5390 false
5391 }
5392 }
5393
5394 if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
5395 let wrapped_point = match direction {
5396 Direction::Next => Point::zero(),
5397 Direction::Prev => snapshot.buffer_snapshot.max_point(),
5398 };
5399 seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
5400 }
5401 }
5402
5403 pub fn go_to_definition(
5404 workspace: &mut Workspace,
5405 _: &GoToDefinition,
5406 cx: &mut ViewContext<Workspace>,
5407 ) {
5408 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
5409 }
5410
5411 pub fn go_to_type_definition(
5412 workspace: &mut Workspace,
5413 _: &GoToTypeDefinition,
5414 cx: &mut ViewContext<Workspace>,
5415 ) {
5416 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
5417 }
5418
5419 fn go_to_definition_of_kind(
5420 kind: GotoDefinitionKind,
5421 workspace: &mut Workspace,
5422 cx: &mut ViewContext<Workspace>,
5423 ) {
5424 let active_item = workspace.active_item(cx);
5425 let editor_handle = if let Some(editor) = active_item
5426 .as_ref()
5427 .and_then(|item| item.act_as::<Self>(cx))
5428 {
5429 editor
5430 } else {
5431 return;
5432 };
5433
5434 let editor = editor_handle.read(cx);
5435 let buffer = editor.buffer.read(cx);
5436 let head = editor.selections.newest::<usize>(cx).head();
5437 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5438 text_anchor
5439 } else {
5440 return;
5441 };
5442
5443 let project = workspace.project().clone();
5444 let definitions = project.update(cx, |project, cx| match kind {
5445 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5446 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5447 });
5448
5449 cx.spawn_labeled("Fetching Definition...", |workspace, mut cx| async move {
5450 let definitions = definitions.await?;
5451 workspace.update(&mut cx, |workspace, cx| {
5452 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
5453 });
5454
5455 Ok::<(), anyhow::Error>(())
5456 })
5457 .detach_and_log_err(cx);
5458 }
5459
5460 pub fn navigate_to_definitions(
5461 workspace: &mut Workspace,
5462 editor_handle: ViewHandle<Editor>,
5463 definitions: Vec<LocationLink>,
5464 cx: &mut ViewContext<Workspace>,
5465 ) {
5466 let pane = workspace.active_pane().clone();
5467 // If there is one definition, just open it directly
5468 if let [definition] = definitions.as_slice() {
5469 let range = definition
5470 .target
5471 .range
5472 .to_offset(definition.target.buffer.read(cx));
5473
5474 let target_editor_handle =
5475 workspace.open_project_item(definition.target.buffer.clone(), cx);
5476 target_editor_handle.update(cx, |target_editor, cx| {
5477 // When selecting a definition in a different buffer, disable the nav history
5478 // to avoid creating a history entry at the previous cursor location.
5479 if editor_handle != target_editor_handle {
5480 pane.update(cx, |pane, _| pane.disable_history());
5481 }
5482 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5483 s.select_ranges([range]);
5484 });
5485
5486 pane.update(cx, |pane, _| pane.enable_history());
5487 });
5488 } else if !definitions.is_empty() {
5489 let replica_id = editor_handle.read(cx).replica_id(cx);
5490 let title = definitions
5491 .iter()
5492 .find(|definition| definition.origin.is_some())
5493 .and_then(|definition| {
5494 definition.origin.as_ref().map(|origin| {
5495 let buffer = origin.buffer.read(cx);
5496 format!(
5497 "Definitions for {}",
5498 buffer
5499 .text_for_range(origin.range.clone())
5500 .collect::<String>()
5501 )
5502 })
5503 })
5504 .unwrap_or("Definitions".to_owned());
5505 let locations = definitions
5506 .into_iter()
5507 .map(|definition| definition.target)
5508 .collect();
5509 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5510 }
5511 }
5512
5513 pub fn find_all_references(
5514 workspace: &mut Workspace,
5515 _: &FindAllReferences,
5516 cx: &mut ViewContext<Workspace>,
5517 ) -> Option<Task<Result<()>>> {
5518 let active_item = workspace.active_item(cx)?;
5519 let editor_handle = active_item.act_as::<Self>(cx)?;
5520
5521 let editor = editor_handle.read(cx);
5522 let buffer = editor.buffer.read(cx);
5523 let head = editor.selections.newest::<usize>(cx).head();
5524 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5525 let replica_id = editor.replica_id(cx);
5526
5527 let project = workspace.project().clone();
5528 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5529 Some(cx.spawn_labeled(
5530 "Finding All References...",
5531 |workspace, mut cx| async move {
5532 let locations = references.await?;
5533 if locations.is_empty() {
5534 return Ok(());
5535 }
5536
5537 workspace.update(&mut cx, |workspace, cx| {
5538 let title = locations
5539 .first()
5540 .as_ref()
5541 .map(|location| {
5542 let buffer = location.buffer.read(cx);
5543 format!(
5544 "References to `{}`",
5545 buffer
5546 .text_for_range(location.range.clone())
5547 .collect::<String>()
5548 )
5549 })
5550 .unwrap();
5551 Self::open_locations_in_multibuffer(
5552 workspace, locations, replica_id, title, cx,
5553 );
5554 });
5555
5556 Ok(())
5557 },
5558 ))
5559 }
5560
5561 /// Opens a multibuffer with the given project locations in it
5562 pub fn open_locations_in_multibuffer(
5563 workspace: &mut Workspace,
5564 mut locations: Vec<Location>,
5565 replica_id: ReplicaId,
5566 title: String,
5567 cx: &mut ViewContext<Workspace>,
5568 ) {
5569 // If there are multiple definitions, open them in a multibuffer
5570 locations.sort_by_key(|location| location.buffer.id());
5571 let mut locations = locations.into_iter().peekable();
5572 let mut ranges_to_highlight = Vec::new();
5573
5574 let excerpt_buffer = cx.add_model(|cx| {
5575 let mut multibuffer = MultiBuffer::new(replica_id);
5576 while let Some(location) = locations.next() {
5577 let buffer = location.buffer.read(cx);
5578 let mut ranges_for_buffer = Vec::new();
5579 let range = location.range.to_offset(buffer);
5580 ranges_for_buffer.push(range.clone());
5581
5582 while let Some(next_location) = locations.peek() {
5583 if next_location.buffer == location.buffer {
5584 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5585 locations.next();
5586 } else {
5587 break;
5588 }
5589 }
5590
5591 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5592 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5593 location.buffer.clone(),
5594 ranges_for_buffer,
5595 1,
5596 cx,
5597 ))
5598 }
5599
5600 multibuffer.with_title(title)
5601 });
5602
5603 let editor = cx.add_view(|cx| {
5604 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5605 });
5606 editor.update(cx, |editor, cx| {
5607 editor.highlight_background::<Self>(
5608 ranges_to_highlight,
5609 |theme| theme.editor.highlighted_line_background,
5610 cx,
5611 );
5612 });
5613 workspace.add_item(Box::new(editor), cx);
5614 }
5615
5616 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5617 use language::ToOffset as _;
5618
5619 let project = self.project.clone()?;
5620 let selection = self.selections.newest_anchor().clone();
5621 let (cursor_buffer, cursor_buffer_position) = self
5622 .buffer
5623 .read(cx)
5624 .text_anchor_for_position(selection.head(), cx)?;
5625 let (tail_buffer, _) = self
5626 .buffer
5627 .read(cx)
5628 .text_anchor_for_position(selection.tail(), cx)?;
5629 if tail_buffer != cursor_buffer {
5630 return None;
5631 }
5632
5633 let snapshot = cursor_buffer.read(cx).snapshot();
5634 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5635 let prepare_rename = project.update(cx, |project, cx| {
5636 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5637 });
5638
5639 Some(cx.spawn(|this, mut cx| async move {
5640 let rename_range = if let Some(range) = prepare_rename.await? {
5641 Some(range)
5642 } else {
5643 this.read_with(&cx, |this, cx| {
5644 let buffer = this.buffer.read(cx).snapshot(cx);
5645 let mut buffer_highlights = this
5646 .document_highlights_for_position(selection.head(), &buffer)
5647 .filter(|highlight| {
5648 highlight.start.excerpt_id() == selection.head().excerpt_id()
5649 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5650 });
5651 buffer_highlights
5652 .next()
5653 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5654 })
5655 };
5656 if let Some(rename_range) = rename_range {
5657 let rename_buffer_range = rename_range.to_offset(&snapshot);
5658 let cursor_offset_in_rename_range =
5659 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5660
5661 this.update(&mut cx, |this, cx| {
5662 this.take_rename(false, cx);
5663 let style = this.style(cx);
5664 let buffer = this.buffer.read(cx).read(cx);
5665 let cursor_offset = selection.head().to_offset(&buffer);
5666 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5667 let rename_end = rename_start + rename_buffer_range.len();
5668 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5669 let mut old_highlight_id = None;
5670 let old_name: Arc<str> = buffer
5671 .chunks(rename_start..rename_end, true)
5672 .map(|chunk| {
5673 if old_highlight_id.is_none() {
5674 old_highlight_id = chunk.syntax_highlight_id;
5675 }
5676 chunk.text
5677 })
5678 .collect::<String>()
5679 .into();
5680
5681 drop(buffer);
5682
5683 // Position the selection in the rename editor so that it matches the current selection.
5684 this.show_local_selections = false;
5685 let rename_editor = cx.add_view(|cx| {
5686 let mut editor = Editor::single_line(None, cx);
5687 if let Some(old_highlight_id) = old_highlight_id {
5688 editor.override_text_style =
5689 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5690 }
5691 editor.buffer.update(cx, |buffer, cx| {
5692 buffer.edit([(0..0, old_name.clone())], None, cx)
5693 });
5694 editor.select_all(&SelectAll, cx);
5695 editor
5696 });
5697
5698 let ranges = this
5699 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5700 .into_iter()
5701 .flat_map(|(_, ranges)| ranges)
5702 .chain(
5703 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5704 .into_iter()
5705 .flat_map(|(_, ranges)| ranges),
5706 )
5707 .collect();
5708
5709 this.highlight_text::<Rename>(
5710 ranges,
5711 HighlightStyle {
5712 fade_out: Some(style.rename_fade),
5713 ..Default::default()
5714 },
5715 cx,
5716 );
5717 cx.focus(&rename_editor);
5718 let block_id = this.insert_blocks(
5719 [BlockProperties {
5720 style: BlockStyle::Flex,
5721 position: range.start.clone(),
5722 height: 1,
5723 render: Arc::new({
5724 let editor = rename_editor.clone();
5725 move |cx: &mut BlockContext| {
5726 ChildView::new(editor.clone(), cx)
5727 .contained()
5728 .with_padding_left(cx.anchor_x)
5729 .boxed()
5730 }
5731 }),
5732 disposition: BlockDisposition::Below,
5733 }],
5734 cx,
5735 )[0];
5736 this.pending_rename = Some(RenameState {
5737 range,
5738 old_name,
5739 editor: rename_editor,
5740 block_id,
5741 });
5742 });
5743 }
5744
5745 Ok(())
5746 }))
5747 }
5748
5749 pub fn confirm_rename(
5750 workspace: &mut Workspace,
5751 _: &ConfirmRename,
5752 cx: &mut ViewContext<Workspace>,
5753 ) -> Option<Task<Result<()>>> {
5754 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5755
5756 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5757 let rename = editor.take_rename(false, cx)?;
5758 let buffer = editor.buffer.read(cx);
5759 let (start_buffer, start) =
5760 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5761 let (end_buffer, end) =
5762 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5763 if start_buffer == end_buffer {
5764 let new_name = rename.editor.read(cx).text(cx);
5765 Some((start_buffer, start..end, rename.old_name, new_name))
5766 } else {
5767 None
5768 }
5769 })?;
5770
5771 let rename = workspace.project().clone().update(cx, |project, cx| {
5772 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5773 });
5774
5775 Some(cx.spawn(|workspace, mut cx| async move {
5776 let project_transaction = rename.await?;
5777 Self::open_project_transaction(
5778 editor.clone(),
5779 workspace,
5780 project_transaction,
5781 format!("Rename: {} → {}", old_name, new_name),
5782 cx.clone(),
5783 )
5784 .await?;
5785
5786 editor.update(&mut cx, |editor, cx| {
5787 editor.refresh_document_highlights(cx);
5788 });
5789 Ok(())
5790 }))
5791 }
5792
5793 fn take_rename(
5794 &mut self,
5795 moving_cursor: bool,
5796 cx: &mut ViewContext<Self>,
5797 ) -> Option<RenameState> {
5798 let rename = self.pending_rename.take()?;
5799 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5800 self.clear_text_highlights::<Rename>(cx);
5801 self.show_local_selections = true;
5802
5803 if moving_cursor {
5804 let rename_editor = rename.editor.read(cx);
5805 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5806
5807 // Update the selection to match the position of the selection inside
5808 // the rename editor.
5809 let snapshot = self.buffer.read(cx).read(cx);
5810 let rename_range = rename.range.to_offset(&snapshot);
5811 let cursor_in_editor = snapshot
5812 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5813 .min(rename_range.end);
5814 drop(snapshot);
5815
5816 self.change_selections(None, cx, |s| {
5817 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5818 });
5819 } else {
5820 self.refresh_document_highlights(cx);
5821 }
5822
5823 Some(rename)
5824 }
5825
5826 #[cfg(any(test, feature = "test-support"))]
5827 pub fn pending_rename(&self) -> Option<&RenameState> {
5828 self.pending_rename.as_ref()
5829 }
5830
5831 fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
5832 let project = match &self.project {
5833 Some(project) => project.clone(),
5834 None => return None,
5835 };
5836
5837 Some(self.perform_format(project, FormatTrigger::Manual, cx))
5838 }
5839
5840 fn perform_format(
5841 &mut self,
5842 project: ModelHandle<Project>,
5843 trigger: FormatTrigger,
5844 cx: &mut ViewContext<'_, Self>,
5845 ) -> Task<Result<()>> {
5846 let buffer = self.buffer().clone();
5847 let buffers = buffer.read(cx).all_buffers();
5848
5849 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5850 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
5851
5852 cx.spawn(|_, mut cx| async move {
5853 let transaction = futures::select_biased! {
5854 _ = timeout => {
5855 log::warn!("timed out waiting for formatting");
5856 None
5857 }
5858 transaction = format.log_err().fuse() => transaction,
5859 };
5860
5861 buffer.update(&mut cx, |buffer, cx| {
5862 if let Some(transaction) = transaction {
5863 if !buffer.is_singleton() {
5864 buffer.push_transaction(&transaction.0);
5865 }
5866 }
5867
5868 cx.notify();
5869 });
5870
5871 Ok(())
5872 })
5873 }
5874
5875 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5876 if let Some(project) = self.project.clone() {
5877 self.buffer.update(cx, |multi_buffer, cx| {
5878 project.update(cx, |project, cx| {
5879 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5880 });
5881 })
5882 }
5883 }
5884
5885 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5886 cx.show_character_palette();
5887 }
5888
5889 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5890 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5891 let buffer = self.buffer.read(cx).snapshot(cx);
5892 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5893 let is_valid = buffer
5894 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5895 .any(|entry| {
5896 entry.diagnostic.is_primary
5897 && !entry.range.is_empty()
5898 && entry.range.start == primary_range_start
5899 && entry.diagnostic.message == active_diagnostics.primary_message
5900 });
5901
5902 if is_valid != active_diagnostics.is_valid {
5903 active_diagnostics.is_valid = is_valid;
5904 let mut new_styles = HashMap::default();
5905 for (block_id, diagnostic) in &active_diagnostics.blocks {
5906 new_styles.insert(
5907 *block_id,
5908 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5909 );
5910 }
5911 self.display_map
5912 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5913 }
5914 }
5915 }
5916
5917 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5918 self.dismiss_diagnostics(cx);
5919 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5920 let buffer = self.buffer.read(cx).snapshot(cx);
5921
5922 let mut primary_range = None;
5923 let mut primary_message = None;
5924 let mut group_end = Point::zero();
5925 let diagnostic_group = buffer
5926 .diagnostic_group::<Point>(group_id)
5927 .map(|entry| {
5928 if entry.range.end > group_end {
5929 group_end = entry.range.end;
5930 }
5931 if entry.diagnostic.is_primary {
5932 primary_range = Some(entry.range.clone());
5933 primary_message = Some(entry.diagnostic.message.clone());
5934 }
5935 entry
5936 })
5937 .collect::<Vec<_>>();
5938 let primary_range = primary_range?;
5939 let primary_message = primary_message?;
5940 let primary_range =
5941 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5942
5943 let blocks = display_map
5944 .insert_blocks(
5945 diagnostic_group.iter().map(|entry| {
5946 let diagnostic = entry.diagnostic.clone();
5947 let message_height = diagnostic.message.lines().count() as u8;
5948 BlockProperties {
5949 style: BlockStyle::Fixed,
5950 position: buffer.anchor_after(entry.range.start),
5951 height: message_height,
5952 render: diagnostic_block_renderer(diagnostic, true),
5953 disposition: BlockDisposition::Below,
5954 }
5955 }),
5956 cx,
5957 )
5958 .into_iter()
5959 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5960 .collect();
5961
5962 Some(ActiveDiagnosticGroup {
5963 primary_range,
5964 primary_message,
5965 blocks,
5966 is_valid: true,
5967 })
5968 });
5969 self.active_diagnostics.is_some()
5970 }
5971
5972 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5973 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5974 self.display_map.update(cx, |display_map, cx| {
5975 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5976 });
5977 cx.notify();
5978 }
5979 }
5980
5981 pub fn set_selections_from_remote(
5982 &mut self,
5983 selections: Vec<Selection<Anchor>>,
5984 pending_selection: Option<Selection<Anchor>>,
5985 cx: &mut ViewContext<Self>,
5986 ) {
5987 let old_cursor_position = self.selections.newest_anchor().head();
5988 self.selections.change_with(cx, |s| {
5989 s.select_anchors(selections);
5990 if let Some(pending_selection) = pending_selection {
5991 s.set_pending(pending_selection, SelectMode::Character);
5992 } else {
5993 s.clear_pending();
5994 }
5995 });
5996 self.selections_did_change(false, &old_cursor_position, cx);
5997 }
5998
5999 fn push_to_selection_history(&mut self) {
6000 self.selection_history.push(SelectionHistoryEntry {
6001 selections: self.selections.disjoint_anchors(),
6002 select_next_state: self.select_next_state.clone(),
6003 add_selections_state: self.add_selections_state.clone(),
6004 });
6005 }
6006
6007 pub fn transact(
6008 &mut self,
6009 cx: &mut ViewContext<Self>,
6010 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6011 ) -> Option<TransactionId> {
6012 self.start_transaction_at(Instant::now(), cx);
6013 update(self, cx);
6014 self.end_transaction_at(Instant::now(), cx)
6015 }
6016
6017 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6018 self.end_selection(cx);
6019 if let Some(tx_id) = self
6020 .buffer
6021 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6022 {
6023 self.selection_history
6024 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6025 }
6026 }
6027
6028 fn end_transaction_at(
6029 &mut self,
6030 now: Instant,
6031 cx: &mut ViewContext<Self>,
6032 ) -> Option<TransactionId> {
6033 if let Some(tx_id) = self
6034 .buffer
6035 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6036 {
6037 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6038 *end_selections = Some(self.selections.disjoint_anchors());
6039 } else {
6040 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6041 }
6042
6043 cx.emit(Event::Edited);
6044 Some(tx_id)
6045 } else {
6046 None
6047 }
6048 }
6049
6050 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6051 let mut fold_ranges = Vec::new();
6052
6053 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6054
6055 let selections = self.selections.all::<Point>(cx);
6056 for selection in selections {
6057 let range = selection.range().sorted();
6058 let buffer_start_row = range.start.row;
6059
6060 for row in (0..=range.end.row).rev() {
6061 let fold_range = display_map.foldable_range(row);
6062
6063 if let Some(fold_range) = fold_range {
6064 if fold_range.end.row >= buffer_start_row {
6065 fold_ranges.push(fold_range);
6066 if row <= range.start.row {
6067 break;
6068 }
6069 }
6070 }
6071 }
6072 }
6073
6074 self.fold_ranges(fold_ranges, true, cx);
6075 }
6076
6077 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6078 let buffer_row = fold_at.buffer_row;
6079 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6080
6081 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6082 let autoscroll = self
6083 .selections
6084 .all::<Point>(cx)
6085 .iter()
6086 .any(|selection| fold_range.overlaps(&selection.range()));
6087
6088 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6089 }
6090 }
6091
6092 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6093 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6094 let buffer = &display_map.buffer_snapshot;
6095 let selections = self.selections.all::<Point>(cx);
6096 let ranges = selections
6097 .iter()
6098 .map(|s| {
6099 let range = s.display_range(&display_map).sorted();
6100 let mut start = range.start.to_point(&display_map);
6101 let mut end = range.end.to_point(&display_map);
6102 start.column = 0;
6103 end.column = buffer.line_len(end.row);
6104 start..end
6105 })
6106 .collect::<Vec<_>>();
6107
6108 self.unfold_ranges(ranges, true, true, cx);
6109 }
6110
6111 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6112 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6113
6114 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6115 ..Point::new(
6116 unfold_at.buffer_row,
6117 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6118 );
6119
6120 let autoscroll = self
6121 .selections
6122 .all::<Point>(cx)
6123 .iter()
6124 .any(|selection| selection.range().overlaps(&intersection_range));
6125
6126 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6127 }
6128
6129 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6130 let selections = self.selections.all::<Point>(cx);
6131 let ranges = selections.into_iter().map(|s| s.start..s.end);
6132 self.fold_ranges(ranges, true, cx);
6133 }
6134
6135 pub fn fold_ranges<T: ToOffset + Clone>(
6136 &mut self,
6137 ranges: impl IntoIterator<Item = Range<T>>,
6138 auto_scroll: bool,
6139 cx: &mut ViewContext<Self>,
6140 ) {
6141 let mut ranges = ranges.into_iter().peekable();
6142 if ranges.peek().is_some() {
6143 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6144
6145 if auto_scroll {
6146 self.request_autoscroll(Autoscroll::fit(), cx);
6147 }
6148
6149 cx.notify();
6150 }
6151 }
6152
6153 pub fn unfold_ranges<T: ToOffset + Clone>(
6154 &mut self,
6155 ranges: impl IntoIterator<Item = Range<T>>,
6156 inclusive: bool,
6157 auto_scroll: bool,
6158 cx: &mut ViewContext<Self>,
6159 ) {
6160 let mut ranges = ranges.into_iter().peekable();
6161 if ranges.peek().is_some() {
6162 self.display_map
6163 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6164 if auto_scroll {
6165 self.request_autoscroll(Autoscroll::fit(), cx);
6166 }
6167
6168 cx.notify();
6169 }
6170 }
6171
6172 pub fn gutter_hover(
6173 &mut self,
6174 GutterHover { hovered }: &GutterHover,
6175 cx: &mut ViewContext<Self>,
6176 ) {
6177 self.gutter_hovered = *hovered;
6178 cx.notify();
6179 }
6180
6181 pub fn insert_blocks(
6182 &mut self,
6183 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6184 cx: &mut ViewContext<Self>,
6185 ) -> Vec<BlockId> {
6186 let blocks = self
6187 .display_map
6188 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6189 self.request_autoscroll(Autoscroll::fit(), cx);
6190 blocks
6191 }
6192
6193 pub fn replace_blocks(
6194 &mut self,
6195 blocks: HashMap<BlockId, RenderBlock>,
6196 cx: &mut ViewContext<Self>,
6197 ) {
6198 self.display_map
6199 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6200 self.request_autoscroll(Autoscroll::fit(), cx);
6201 }
6202
6203 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6204 self.display_map.update(cx, |display_map, cx| {
6205 display_map.remove_blocks(block_ids, cx)
6206 });
6207 }
6208
6209 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
6210 self.display_map
6211 .update(cx, |map, cx| map.snapshot(cx))
6212 .longest_row()
6213 }
6214
6215 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
6216 self.display_map
6217 .update(cx, |map, cx| map.snapshot(cx))
6218 .max_point()
6219 }
6220
6221 pub fn text(&self, cx: &AppContext) -> String {
6222 self.buffer.read(cx).read(cx).text()
6223 }
6224
6225 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6226 self.transact(cx, |this, cx| {
6227 this.buffer
6228 .read(cx)
6229 .as_singleton()
6230 .expect("you can only call set_text on editors for singleton buffers")
6231 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6232 });
6233 }
6234
6235 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
6236 self.display_map
6237 .update(cx, |map, cx| map.snapshot(cx))
6238 .text()
6239 }
6240
6241 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6242 let language_name = self
6243 .buffer
6244 .read(cx)
6245 .as_singleton()
6246 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
6247 .map(|l| l.name());
6248
6249 let settings = cx.global::<Settings>();
6250 let mode = self
6251 .soft_wrap_mode_override
6252 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
6253 match mode {
6254 settings::SoftWrap::None => SoftWrap::None,
6255 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6256 settings::SoftWrap::PreferredLineLength => {
6257 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
6258 }
6259 }
6260 }
6261
6262 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
6263 self.soft_wrap_mode_override = Some(mode);
6264 cx.notify();
6265 }
6266
6267 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
6268 self.display_map
6269 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6270 }
6271
6272 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6273 if self.soft_wrap_mode_override.is_some() {
6274 self.soft_wrap_mode_override.take();
6275 } else {
6276 let soft_wrap = match self.soft_wrap_mode(cx) {
6277 SoftWrap::None => settings::SoftWrap::EditorWidth,
6278 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
6279 };
6280 self.soft_wrap_mode_override = Some(soft_wrap);
6281 }
6282 cx.notify();
6283 }
6284
6285 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6286 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6287 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6288 cx.reveal_path(&file.abs_path(cx));
6289 }
6290 }
6291 }
6292
6293 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6294 self.highlighted_rows = rows;
6295 }
6296
6297 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6298 self.highlighted_rows.clone()
6299 }
6300
6301 pub fn highlight_background<T: 'static>(
6302 &mut self,
6303 ranges: Vec<Range<Anchor>>,
6304 color_fetcher: fn(&Theme) -> Color,
6305 cx: &mut ViewContext<Self>,
6306 ) {
6307 self.background_highlights
6308 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6309 cx.notify();
6310 }
6311
6312 #[allow(clippy::type_complexity)]
6313 pub fn clear_background_highlights<T: 'static>(
6314 &mut self,
6315 cx: &mut ViewContext<Self>,
6316 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6317 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6318 if highlights.is_some() {
6319 cx.notify();
6320 }
6321 highlights
6322 }
6323
6324 #[cfg(feature = "test-support")]
6325 pub fn all_background_highlights(
6326 &mut self,
6327 cx: &mut ViewContext<Self>,
6328 ) -> Vec<(Range<DisplayPoint>, Color)> {
6329 let snapshot = self.snapshot(cx);
6330 let buffer = &snapshot.buffer_snapshot;
6331 let start = buffer.anchor_before(0);
6332 let end = buffer.anchor_after(buffer.len());
6333 let theme = cx.global::<Settings>().theme.as_ref();
6334 self.background_highlights_in_range(start..end, &snapshot, theme)
6335 }
6336
6337 fn document_highlights_for_position<'a>(
6338 &'a self,
6339 position: Anchor,
6340 buffer: &'a MultiBufferSnapshot,
6341 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6342 let read_highlights = self
6343 .background_highlights
6344 .get(&TypeId::of::<DocumentHighlightRead>())
6345 .map(|h| &h.1);
6346 let write_highlights = self
6347 .background_highlights
6348 .get(&TypeId::of::<DocumentHighlightWrite>())
6349 .map(|h| &h.1);
6350 let left_position = position.bias_left(buffer);
6351 let right_position = position.bias_right(buffer);
6352 read_highlights
6353 .into_iter()
6354 .chain(write_highlights)
6355 .flat_map(move |ranges| {
6356 let start_ix = match ranges.binary_search_by(|probe| {
6357 let cmp = probe.end.cmp(&left_position, buffer);
6358 if cmp.is_ge() {
6359 Ordering::Greater
6360 } else {
6361 Ordering::Less
6362 }
6363 }) {
6364 Ok(i) | Err(i) => i,
6365 };
6366
6367 let right_position = right_position.clone();
6368 ranges[start_ix..]
6369 .iter()
6370 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6371 })
6372 }
6373
6374 pub fn background_highlights_in_range(
6375 &self,
6376 search_range: Range<Anchor>,
6377 display_snapshot: &DisplaySnapshot,
6378 theme: &Theme,
6379 ) -> Vec<(Range<DisplayPoint>, Color)> {
6380 let mut results = Vec::new();
6381 let buffer = &display_snapshot.buffer_snapshot;
6382 for (color_fetcher, ranges) in self.background_highlights.values() {
6383 let color = color_fetcher(theme);
6384 let start_ix = match ranges.binary_search_by(|probe| {
6385 let cmp = probe.end.cmp(&search_range.start, buffer);
6386 if cmp.is_gt() {
6387 Ordering::Greater
6388 } else {
6389 Ordering::Less
6390 }
6391 }) {
6392 Ok(i) | Err(i) => i,
6393 };
6394 for range in &ranges[start_ix..] {
6395 if range.start.cmp(&search_range.end, buffer).is_ge() {
6396 break;
6397 }
6398 let start = range
6399 .start
6400 .to_point(buffer)
6401 .to_display_point(display_snapshot);
6402 let end = range
6403 .end
6404 .to_point(buffer)
6405 .to_display_point(display_snapshot);
6406 results.push((start..end, color))
6407 }
6408 }
6409 results
6410 }
6411
6412 pub fn highlight_text<T: 'static>(
6413 &mut self,
6414 ranges: Vec<Range<Anchor>>,
6415 style: HighlightStyle,
6416 cx: &mut ViewContext<Self>,
6417 ) {
6418 self.display_map.update(cx, |map, _| {
6419 map.highlight_text(TypeId::of::<T>(), ranges, style)
6420 });
6421 cx.notify();
6422 }
6423
6424 pub fn text_highlights<'a, T: 'static>(
6425 &'a self,
6426 cx: &'a AppContext,
6427 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6428 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6429 }
6430
6431 pub fn clear_text_highlights<T: 'static>(
6432 &mut self,
6433 cx: &mut ViewContext<Self>,
6434 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6435 let highlights = self
6436 .display_map
6437 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6438 if highlights.is_some() {
6439 cx.notify();
6440 }
6441 highlights
6442 }
6443
6444 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6445 self.blink_manager.read(cx).visible() && self.focused
6446 }
6447
6448 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6449 cx.notify();
6450 }
6451
6452 fn on_buffer_event(
6453 &mut self,
6454 _: ModelHandle<MultiBuffer>,
6455 event: &multi_buffer::Event,
6456 cx: &mut ViewContext<Self>,
6457 ) {
6458 match event {
6459 multi_buffer::Event::Edited => {
6460 self.refresh_active_diagnostics(cx);
6461 self.refresh_code_actions(cx);
6462 cx.emit(Event::BufferEdited);
6463 }
6464 multi_buffer::Event::ExcerptsAdded {
6465 buffer,
6466 predecessor,
6467 excerpts,
6468 } => cx.emit(Event::ExcerptsAdded {
6469 buffer: buffer.clone(),
6470 predecessor: *predecessor,
6471 excerpts: excerpts.clone(),
6472 }),
6473 multi_buffer::Event::ExcerptsRemoved { ids } => {
6474 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6475 }
6476 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6477 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6478 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6479 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6480 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6481 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6482 multi_buffer::Event::DiagnosticsUpdated => {
6483 self.refresh_active_diagnostics(cx);
6484 }
6485 }
6486 }
6487
6488 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6489 cx.notify();
6490 }
6491
6492 pub fn set_searchable(&mut self, searchable: bool) {
6493 self.searchable = searchable;
6494 }
6495
6496 pub fn searchable(&self) -> bool {
6497 self.searchable
6498 }
6499
6500 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6501 let active_item = workspace.active_item(cx);
6502 let editor_handle = if let Some(editor) = active_item
6503 .as_ref()
6504 .and_then(|item| item.act_as::<Self>(cx))
6505 {
6506 editor
6507 } else {
6508 cx.propagate_action();
6509 return;
6510 };
6511
6512 let editor = editor_handle.read(cx);
6513 let buffer = editor.buffer.read(cx);
6514 if buffer.is_singleton() {
6515 cx.propagate_action();
6516 return;
6517 }
6518
6519 let mut new_selections_by_buffer = HashMap::default();
6520 for selection in editor.selections.all::<usize>(cx) {
6521 for (buffer, mut range) in
6522 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6523 {
6524 if selection.reversed {
6525 mem::swap(&mut range.start, &mut range.end);
6526 }
6527 new_selections_by_buffer
6528 .entry(buffer)
6529 .or_insert(Vec::new())
6530 .push(range)
6531 }
6532 }
6533
6534 editor_handle.update(cx, |editor, cx| {
6535 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6536 });
6537 let pane = workspace.active_pane().clone();
6538 pane.update(cx, |pane, _| pane.disable_history());
6539
6540 // We defer the pane interaction because we ourselves are a workspace item
6541 // and activating a new item causes the pane to call a method on us reentrantly,
6542 // which panics if we're on the stack.
6543 cx.defer(move |workspace, cx| {
6544 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6545 let editor = workspace.open_project_item::<Self>(buffer, cx);
6546 editor.update(cx, |editor, cx| {
6547 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6548 s.select_ranges(ranges);
6549 });
6550 });
6551 }
6552
6553 pane.update(cx, |pane, _| pane.enable_history());
6554 });
6555 }
6556
6557 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
6558 let editor = workspace.open_path(action.path.clone(), None, true, cx);
6559 let position = action.position;
6560 let anchor = action.anchor;
6561 cx.spawn_weak(|_, mut cx| async move {
6562 let editor = editor.await.log_err()?.downcast::<Editor>()?;
6563 editor.update(&mut cx, |editor, cx| {
6564 let buffer = editor.buffer().read(cx).as_singleton()?;
6565 let buffer = buffer.read(cx);
6566 let cursor = if buffer.can_resolve(&anchor) {
6567 language::ToPoint::to_point(&anchor, buffer)
6568 } else {
6569 buffer.clip_point(position, Bias::Left)
6570 };
6571
6572 let nav_history = editor.nav_history.take();
6573 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6574 s.select_ranges([cursor..cursor]);
6575 });
6576 editor.nav_history = nav_history;
6577
6578 Some(())
6579 })?;
6580 Some(())
6581 })
6582 .detach()
6583 }
6584
6585 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6586 let snapshot = self.buffer.read(cx).read(cx);
6587 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6588 Some(
6589 ranges
6590 .iter()
6591 .map(move |range| {
6592 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6593 })
6594 .collect(),
6595 )
6596 }
6597
6598 fn selection_replacement_ranges(
6599 &self,
6600 range: Range<OffsetUtf16>,
6601 cx: &AppContext,
6602 ) -> Vec<Range<OffsetUtf16>> {
6603 let selections = self.selections.all::<OffsetUtf16>(cx);
6604 let newest_selection = selections
6605 .iter()
6606 .max_by_key(|selection| selection.id)
6607 .unwrap();
6608 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6609 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6610 let snapshot = self.buffer.read(cx).read(cx);
6611 selections
6612 .into_iter()
6613 .map(|mut selection| {
6614 selection.start.0 =
6615 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6616 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6617 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6618 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6619 })
6620 .collect()
6621 }
6622
6623 fn report_event(&self, name: &str, cx: &AppContext) {
6624 if let Some((project, file)) = self.project.as_ref().zip(
6625 self.buffer
6626 .read(cx)
6627 .as_singleton()
6628 .and_then(|b| b.read(cx).file()),
6629 ) {
6630 let extension = Path::new(file.file_name(cx))
6631 .extension()
6632 .and_then(|e| e.to_str());
6633 project.read(cx).client().report_event(
6634 name,
6635 json!({ "File Extension": extension }),
6636 cx.global::<Settings>().telemetry(),
6637 );
6638 }
6639 }
6640
6641 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
6642 /// with each line being an array of {text, highlight} objects.
6643 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
6644 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
6645 return;
6646 };
6647
6648 #[derive(Serialize)]
6649 struct Chunk<'a> {
6650 text: String,
6651 highlight: Option<&'a str>,
6652 }
6653
6654 let snapshot = buffer.read(cx).snapshot();
6655 let range = self
6656 .selected_text_range(cx)
6657 .and_then(|selected_range| {
6658 if selected_range.is_empty() {
6659 None
6660 } else {
6661 Some(selected_range)
6662 }
6663 })
6664 .unwrap_or_else(|| 0..snapshot.len());
6665
6666 let chunks = snapshot.chunks(range, true);
6667 let mut lines = Vec::new();
6668 let mut line: VecDeque<Chunk> = VecDeque::new();
6669
6670 let theme = &cx.global::<Settings>().theme.editor.syntax;
6671
6672 for chunk in chunks {
6673 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
6674 let mut chunk_lines = chunk.text.split("\n").peekable();
6675 while let Some(text) = chunk_lines.next() {
6676 let mut merged_with_last_token = false;
6677 if let Some(last_token) = line.back_mut() {
6678 if last_token.highlight == highlight {
6679 last_token.text.push_str(text);
6680 merged_with_last_token = true;
6681 }
6682 }
6683
6684 if !merged_with_last_token {
6685 line.push_back(Chunk {
6686 text: text.into(),
6687 highlight,
6688 });
6689 }
6690
6691 if chunk_lines.peek().is_some() {
6692 if line.len() > 1 && line.front().unwrap().text.is_empty() {
6693 line.pop_front();
6694 }
6695 if line.len() > 1 && line.back().unwrap().text.is_empty() {
6696 line.pop_back();
6697 }
6698
6699 lines.push(mem::take(&mut line));
6700 }
6701 }
6702 }
6703
6704 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
6705 cx.write_to_clipboard(ClipboardItem::new(lines));
6706 }
6707}
6708
6709fn consume_contiguous_rows(
6710 contiguous_row_selections: &mut Vec<Selection<Point>>,
6711 selection: &Selection<Point>,
6712 display_map: &DisplaySnapshot,
6713 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
6714) -> (u32, u32) {
6715 contiguous_row_selections.push(selection.clone());
6716 let start_row = selection.start.row;
6717 let mut end_row = ending_row(selection, display_map);
6718
6719 while let Some(next_selection) = selections.peek() {
6720 if next_selection.start.row <= end_row {
6721 end_row = ending_row(next_selection, display_map);
6722 contiguous_row_selections.push(selections.next().unwrap().clone());
6723 } else {
6724 break;
6725 }
6726 }
6727 (start_row, end_row)
6728}
6729
6730fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
6731 if next_selection.end.column > 0 || next_selection.is_empty() {
6732 display_map.next_line_boundary(next_selection.end).0.row + 1
6733 } else {
6734 next_selection.end.row
6735 }
6736}
6737
6738impl EditorSnapshot {
6739 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6740 self.display_snapshot.buffer_snapshot.language_at(position)
6741 }
6742
6743 pub fn is_focused(&self) -> bool {
6744 self.is_focused
6745 }
6746
6747 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6748 self.placeholder_text.as_ref()
6749 }
6750
6751 pub fn scroll_position(&self) -> Vector2F {
6752 self.scroll_anchor.scroll_position(&self.display_snapshot)
6753 }
6754}
6755
6756impl Deref for EditorSnapshot {
6757 type Target = DisplaySnapshot;
6758
6759 fn deref(&self) -> &Self::Target {
6760 &self.display_snapshot
6761 }
6762}
6763
6764#[derive(Clone, Debug, PartialEq, Eq)]
6765pub enum Event {
6766 InputIgnored {
6767 text: Arc<str>,
6768 },
6769 ExcerptsAdded {
6770 buffer: ModelHandle<Buffer>,
6771 predecessor: ExcerptId,
6772 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
6773 },
6774 ExcerptsRemoved {
6775 ids: Vec<ExcerptId>,
6776 },
6777 BufferEdited,
6778 Edited,
6779 Reparsed,
6780 Blurred,
6781 DirtyChanged,
6782 Saved,
6783 TitleChanged,
6784 SelectionsChanged {
6785 local: bool,
6786 },
6787 ScrollPositionChanged {
6788 local: bool,
6789 },
6790 Closed,
6791}
6792
6793pub struct EditorFocused(pub ViewHandle<Editor>);
6794pub struct EditorBlurred(pub ViewHandle<Editor>);
6795pub struct EditorReleased(pub WeakViewHandle<Editor>);
6796
6797impl Entity for Editor {
6798 type Event = Event;
6799
6800 fn release(&mut self, cx: &mut MutableAppContext) {
6801 cx.emit_global(EditorReleased(self.handle.clone()));
6802 }
6803}
6804
6805impl View for Editor {
6806 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6807 let style = self.style(cx);
6808 let font_changed = self.display_map.update(cx, |map, cx| {
6809 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
6810 map.set_font(style.text.font_id, style.text.font_size, cx)
6811 });
6812
6813 if font_changed {
6814 let handle = self.handle.clone();
6815 cx.defer(move |cx| {
6816 if let Some(editor) = handle.upgrade(cx) {
6817 editor.update(cx, |editor, cx| {
6818 hide_hover(editor, &HideHover, cx);
6819 hide_link_definition(editor, cx);
6820 })
6821 }
6822 });
6823 }
6824
6825 Stack::new()
6826 .with_child(EditorElement::new(self.handle.clone(), style.clone()).boxed())
6827 .with_child(ChildView::new(&self.mouse_context_menu, cx).boxed())
6828 .boxed()
6829 }
6830
6831 fn ui_name() -> &'static str {
6832 "Editor"
6833 }
6834
6835 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6836 if cx.is_self_focused() {
6837 let focused_event = EditorFocused(cx.handle());
6838 cx.emit_global(focused_event);
6839 }
6840 if let Some(rename) = self.pending_rename.as_ref() {
6841 cx.focus(&rename.editor);
6842 } else {
6843 if !self.focused {
6844 self.blink_manager.update(cx, BlinkManager::enable);
6845 }
6846 self.focused = true;
6847 self.buffer.update(cx, |buffer, cx| {
6848 buffer.finalize_last_transaction(cx);
6849 if self.leader_replica_id.is_none() {
6850 buffer.set_active_selections(
6851 &self.selections.disjoint_anchors(),
6852 self.selections.line_mode,
6853 self.cursor_shape,
6854 cx,
6855 );
6856 }
6857 });
6858 }
6859 }
6860
6861 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6862 let blurred_event = EditorBlurred(cx.handle());
6863 cx.emit_global(blurred_event);
6864 self.focused = false;
6865 self.blink_manager.update(cx, BlinkManager::disable);
6866 self.buffer
6867 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6868 self.hide_context_menu(cx);
6869 hide_hover(self, &HideHover, cx);
6870 cx.emit(Event::Blurred);
6871 cx.notify();
6872 }
6873
6874 fn modifiers_changed(
6875 &mut self,
6876 event: &gpui::ModifiersChangedEvent,
6877 cx: &mut ViewContext<Self>,
6878 ) -> bool {
6879 let pending_selection = self.has_pending_selection();
6880
6881 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
6882 if event.cmd && !pending_selection {
6883 let snapshot = self.snapshot(cx);
6884 let kind = if event.shift {
6885 LinkDefinitionKind::Type
6886 } else {
6887 LinkDefinitionKind::Symbol
6888 };
6889
6890 show_link_definition(kind, self, point, snapshot, cx);
6891 return false;
6892 }
6893 }
6894
6895 {
6896 if self.link_go_to_definition_state.symbol_range.is_some()
6897 || !self.link_go_to_definition_state.definitions.is_empty()
6898 {
6899 self.link_go_to_definition_state.symbol_range.take();
6900 self.link_go_to_definition_state.definitions.clear();
6901 cx.notify();
6902 }
6903
6904 self.link_go_to_definition_state.task = None;
6905
6906 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
6907 }
6908
6909 false
6910 }
6911
6912 fn keymap_context(&self, _: &AppContext) -> KeymapContext {
6913 let mut context = Self::default_keymap_context();
6914 let mode = match self.mode {
6915 EditorMode::SingleLine => "single_line",
6916 EditorMode::AutoHeight { .. } => "auto_height",
6917 EditorMode::Full => "full",
6918 };
6919 context.add_key("mode", mode);
6920 if self.pending_rename.is_some() {
6921 context.add_identifier("renaming");
6922 }
6923 match self.context_menu.as_ref() {
6924 Some(ContextMenu::Completions(_)) => context.add_identifier("showing_completions"),
6925 Some(ContextMenu::CodeActions(_)) => context.add_identifier("showing_code_actions"),
6926 None => {}
6927 }
6928
6929 for layer in self.keymap_context_layers.values() {
6930 context.extend(layer);
6931 }
6932
6933 context
6934 }
6935
6936 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6937 Some(
6938 self.buffer
6939 .read(cx)
6940 .read(cx)
6941 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6942 .collect(),
6943 )
6944 }
6945
6946 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6947 // Prevent the IME menu from appearing when holding down an alphabetic key
6948 // while input is disabled.
6949 if !self.input_enabled {
6950 return None;
6951 }
6952
6953 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6954 Some(range.start.0..range.end.0)
6955 }
6956
6957 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6958 let snapshot = self.buffer.read(cx).read(cx);
6959 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6960 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6961 }
6962
6963 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6964 self.clear_text_highlights::<InputComposition>(cx);
6965 self.ime_transaction.take();
6966 }
6967
6968 fn replace_text_in_range(
6969 &mut self,
6970 range_utf16: Option<Range<usize>>,
6971 text: &str,
6972 cx: &mut ViewContext<Self>,
6973 ) {
6974 self.transact(cx, |this, cx| {
6975 if this.input_enabled {
6976 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6977 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6978 Some(this.selection_replacement_ranges(range_utf16, cx))
6979 } else {
6980 this.marked_text_ranges(cx)
6981 };
6982
6983 if let Some(new_selected_ranges) = new_selected_ranges {
6984 this.change_selections(None, cx, |selections| {
6985 selections.select_ranges(new_selected_ranges)
6986 });
6987 }
6988 }
6989
6990 this.handle_input(text, cx);
6991 });
6992
6993 if !self.input_enabled {
6994 return;
6995 }
6996
6997 if let Some(transaction) = self.ime_transaction {
6998 self.buffer.update(cx, |buffer, cx| {
6999 buffer.group_until_transaction(transaction, cx);
7000 });
7001 }
7002
7003 self.unmark_text(cx);
7004 }
7005
7006 fn replace_and_mark_text_in_range(
7007 &mut self,
7008 range_utf16: Option<Range<usize>>,
7009 text: &str,
7010 new_selected_range_utf16: Option<Range<usize>>,
7011 cx: &mut ViewContext<Self>,
7012 ) {
7013 if !self.input_enabled {
7014 return;
7015 }
7016
7017 let transaction = self.transact(cx, |this, cx| {
7018 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7019 let snapshot = this.buffer.read(cx).read(cx);
7020 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7021 for marked_range in &mut marked_ranges {
7022 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7023 marked_range.start.0 += relative_range_utf16.start;
7024 marked_range.start =
7025 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7026 marked_range.end =
7027 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7028 }
7029 }
7030 Some(marked_ranges)
7031 } else if let Some(range_utf16) = range_utf16 {
7032 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7033 Some(this.selection_replacement_ranges(range_utf16, cx))
7034 } else {
7035 None
7036 };
7037
7038 if let Some(ranges) = ranges_to_replace {
7039 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7040 }
7041
7042 let marked_ranges = {
7043 let snapshot = this.buffer.read(cx).read(cx);
7044 this.selections
7045 .disjoint_anchors()
7046 .iter()
7047 .map(|selection| {
7048 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7049 })
7050 .collect::<Vec<_>>()
7051 };
7052
7053 if text.is_empty() {
7054 this.unmark_text(cx);
7055 } else {
7056 this.highlight_text::<InputComposition>(
7057 marked_ranges.clone(),
7058 this.style(cx).composition_mark,
7059 cx,
7060 );
7061 }
7062
7063 this.handle_input(text, cx);
7064
7065 if let Some(new_selected_range) = new_selected_range_utf16 {
7066 let snapshot = this.buffer.read(cx).read(cx);
7067 let new_selected_ranges = marked_ranges
7068 .into_iter()
7069 .map(|marked_range| {
7070 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7071 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7072 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7073 snapshot.clip_offset_utf16(new_start, Bias::Left)
7074 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7075 })
7076 .collect::<Vec<_>>();
7077
7078 drop(snapshot);
7079 this.change_selections(None, cx, |selections| {
7080 selections.select_ranges(new_selected_ranges)
7081 });
7082 }
7083 });
7084
7085 self.ime_transaction = self.ime_transaction.or(transaction);
7086 if let Some(transaction) = self.ime_transaction {
7087 self.buffer.update(cx, |buffer, cx| {
7088 buffer.group_until_transaction(transaction, cx);
7089 });
7090 }
7091
7092 if self.text_highlights::<InputComposition>(cx).is_none() {
7093 self.ime_transaction.take();
7094 }
7095 }
7096}
7097
7098fn build_style(
7099 settings: &Settings,
7100 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7101 override_text_style: Option<&OverrideTextStyle>,
7102 cx: &AppContext,
7103) -> EditorStyle {
7104 let font_cache = cx.font_cache();
7105
7106 let mut theme = settings.theme.editor.clone();
7107 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7108 let field_editor_theme = get_field_editor_theme(&settings.theme);
7109 theme.text_color = field_editor_theme.text.color;
7110 theme.selection = field_editor_theme.selection;
7111 theme.background = field_editor_theme
7112 .container
7113 .background_color
7114 .unwrap_or_default();
7115 EditorStyle {
7116 text: field_editor_theme.text,
7117 placeholder_text: field_editor_theme.placeholder_text,
7118 theme,
7119 }
7120 } else {
7121 let font_family_id = settings.buffer_font_family;
7122 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7123 let font_properties = Default::default();
7124 let font_id = font_cache
7125 .select_font(font_family_id, &font_properties)
7126 .unwrap();
7127 let font_size = settings.buffer_font_size;
7128 EditorStyle {
7129 text: TextStyle {
7130 color: settings.theme.editor.text_color,
7131 font_family_name,
7132 font_family_id,
7133 font_id,
7134 font_size,
7135 font_properties,
7136 underline: Default::default(),
7137 },
7138 placeholder_text: None,
7139 theme,
7140 }
7141 };
7142
7143 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7144 if let Some(highlighted) = style
7145 .text
7146 .clone()
7147 .highlight(highlight_style, font_cache)
7148 .log_err()
7149 {
7150 style.text = highlighted;
7151 }
7152 }
7153
7154 style
7155}
7156
7157trait SelectionExt {
7158 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7159 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7160 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7161 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7162 -> Range<u32>;
7163}
7164
7165impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7166 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7167 let start = self.start.to_point(buffer);
7168 let end = self.end.to_point(buffer);
7169 if self.reversed {
7170 end..start
7171 } else {
7172 start..end
7173 }
7174 }
7175
7176 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7177 let start = self.start.to_offset(buffer);
7178 let end = self.end.to_offset(buffer);
7179 if self.reversed {
7180 end..start
7181 } else {
7182 start..end
7183 }
7184 }
7185
7186 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7187 let start = self
7188 .start
7189 .to_point(&map.buffer_snapshot)
7190 .to_display_point(map);
7191 let end = self
7192 .end
7193 .to_point(&map.buffer_snapshot)
7194 .to_display_point(map);
7195 if self.reversed {
7196 end..start
7197 } else {
7198 start..end
7199 }
7200 }
7201
7202 fn spanned_rows(
7203 &self,
7204 include_end_if_at_line_start: bool,
7205 map: &DisplaySnapshot,
7206 ) -> Range<u32> {
7207 let start = self.start.to_point(&map.buffer_snapshot);
7208 let mut end = self.end.to_point(&map.buffer_snapshot);
7209 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7210 end.row -= 1;
7211 }
7212
7213 let buffer_start = map.prev_line_boundary(start).0;
7214 let buffer_end = map.next_line_boundary(end).0;
7215 buffer_start.row..buffer_end.row + 1
7216 }
7217}
7218
7219impl<T: InvalidationRegion> InvalidationStack<T> {
7220 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7221 where
7222 S: Clone + ToOffset,
7223 {
7224 while let Some(region) = self.last() {
7225 let all_selections_inside_invalidation_ranges =
7226 if selections.len() == region.ranges().len() {
7227 selections
7228 .iter()
7229 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7230 .all(|(selection, invalidation_range)| {
7231 let head = selection.head().to_offset(buffer);
7232 invalidation_range.start <= head && invalidation_range.end >= head
7233 })
7234 } else {
7235 false
7236 };
7237
7238 if all_selections_inside_invalidation_ranges {
7239 break;
7240 } else {
7241 self.pop();
7242 }
7243 }
7244 }
7245}
7246
7247impl<T> Default for InvalidationStack<T> {
7248 fn default() -> Self {
7249 Self(Default::default())
7250 }
7251}
7252
7253impl<T> Deref for InvalidationStack<T> {
7254 type Target = Vec<T>;
7255
7256 fn deref(&self) -> &Self::Target {
7257 &self.0
7258 }
7259}
7260
7261impl<T> DerefMut for InvalidationStack<T> {
7262 fn deref_mut(&mut self) -> &mut Self::Target {
7263 &mut self.0
7264 }
7265}
7266
7267impl InvalidationRegion for SnippetState {
7268 fn ranges(&self) -> &[Range<Anchor>] {
7269 &self.ranges[self.active_index]
7270 }
7271}
7272
7273impl Deref for EditorStyle {
7274 type Target = theme::Editor;
7275
7276 fn deref(&self) -> &Self::Target {
7277 &self.theme
7278 }
7279}
7280
7281pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7282 let mut highlighted_lines = Vec::new();
7283 for line in diagnostic.message.lines() {
7284 highlighted_lines.push(highlight_diagnostic_message(line));
7285 }
7286
7287 Arc::new(move |cx: &mut BlockContext| {
7288 let settings = cx.global::<Settings>();
7289 let theme = &settings.theme.editor;
7290 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7291 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
7292 Flex::column()
7293 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7294 Label::new(
7295 line.clone(),
7296 style.message.clone().with_font_size(font_size),
7297 )
7298 .with_highlights(highlights.clone())
7299 .contained()
7300 .with_margin_left(cx.anchor_x)
7301 .boxed()
7302 }))
7303 .aligned()
7304 .left()
7305 .boxed()
7306 })
7307}
7308
7309pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
7310 let mut message_without_backticks = String::new();
7311 let mut prev_offset = 0;
7312 let mut inside_block = false;
7313 let mut highlights = Vec::new();
7314 for (match_ix, (offset, _)) in message
7315 .match_indices('`')
7316 .chain([(message.len(), "")])
7317 .enumerate()
7318 {
7319 message_without_backticks.push_str(&message[prev_offset..offset]);
7320 if inside_block {
7321 highlights.extend(prev_offset - match_ix..offset - match_ix);
7322 }
7323
7324 inside_block = !inside_block;
7325 prev_offset = offset + 1;
7326 }
7327
7328 (message_without_backticks, highlights)
7329}
7330
7331pub fn diagnostic_style(
7332 severity: DiagnosticSeverity,
7333 valid: bool,
7334 theme: &theme::Editor,
7335) -> DiagnosticStyle {
7336 match (severity, valid) {
7337 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7338 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7339 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7340 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
7341 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
7342 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
7343 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
7344 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
7345 _ => theme.invalid_hint_diagnostic.clone(),
7346 }
7347}
7348
7349pub fn combine_syntax_and_fuzzy_match_highlights(
7350 text: &str,
7351 default_style: HighlightStyle,
7352 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
7353 match_indices: &[usize],
7354) -> Vec<(Range<usize>, HighlightStyle)> {
7355 let mut result = Vec::new();
7356 let mut match_indices = match_indices.iter().copied().peekable();
7357
7358 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
7359 {
7360 syntax_highlight.weight = None;
7361
7362 // Add highlights for any fuzzy match characters before the next
7363 // syntax highlight range.
7364 while let Some(&match_index) = match_indices.peek() {
7365 if match_index >= range.start {
7366 break;
7367 }
7368 match_indices.next();
7369 let end_index = char_ix_after(match_index, text);
7370 let mut match_style = default_style;
7371 match_style.weight = Some(fonts::Weight::BOLD);
7372 result.push((match_index..end_index, match_style));
7373 }
7374
7375 if range.start == usize::MAX {
7376 break;
7377 }
7378
7379 // Add highlights for any fuzzy match characters within the
7380 // syntax highlight range.
7381 let mut offset = range.start;
7382 while let Some(&match_index) = match_indices.peek() {
7383 if match_index >= range.end {
7384 break;
7385 }
7386
7387 match_indices.next();
7388 if match_index > offset {
7389 result.push((offset..match_index, syntax_highlight));
7390 }
7391
7392 let mut end_index = char_ix_after(match_index, text);
7393 while let Some(&next_match_index) = match_indices.peek() {
7394 if next_match_index == end_index && next_match_index < range.end {
7395 end_index = char_ix_after(next_match_index, text);
7396 match_indices.next();
7397 } else {
7398 break;
7399 }
7400 }
7401
7402 let mut match_style = syntax_highlight;
7403 match_style.weight = Some(fonts::Weight::BOLD);
7404 result.push((match_index..end_index, match_style));
7405 offset = end_index;
7406 }
7407
7408 if offset < range.end {
7409 result.push((offset..range.end, syntax_highlight));
7410 }
7411 }
7412
7413 fn char_ix_after(ix: usize, text: &str) -> usize {
7414 ix + text[ix..].chars().next().unwrap().len_utf8()
7415 }
7416
7417 result
7418}
7419
7420pub fn styled_runs_for_code_label<'a>(
7421 label: &'a CodeLabel,
7422 syntax_theme: &'a theme::SyntaxTheme,
7423) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
7424 let fade_out = HighlightStyle {
7425 fade_out: Some(0.35),
7426 ..Default::default()
7427 };
7428
7429 let mut prev_end = label.filter_range.end;
7430 label
7431 .runs
7432 .iter()
7433 .enumerate()
7434 .flat_map(move |(ix, (range, highlight_id))| {
7435 let style = if let Some(style) = highlight_id.style(syntax_theme) {
7436 style
7437 } else {
7438 return Default::default();
7439 };
7440 let mut muted_style = style;
7441 muted_style.highlight(fade_out);
7442
7443 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
7444 if range.start >= label.filter_range.end {
7445 if range.start > prev_end {
7446 runs.push((prev_end..range.start, fade_out));
7447 }
7448 runs.push((range.clone(), muted_style));
7449 } else if range.end <= label.filter_range.end {
7450 runs.push((range.clone(), style));
7451 } else {
7452 runs.push((range.start..label.filter_range.end, style));
7453 runs.push((label.filter_range.end..range.end, muted_style));
7454 }
7455 prev_end = cmp::max(prev_end, range.end);
7456
7457 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
7458 runs.push((prev_end..label.text.len(), fade_out));
7459 }
7460
7461 runs
7462 })
7463}
7464
7465pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
7466 let mut index = 0;
7467 let mut codepoints = text.char_indices().peekable();
7468
7469 std::iter::from_fn(move || {
7470 let start_index = index;
7471 while let Some((new_index, codepoint)) = codepoints.next() {
7472 index = new_index + codepoint.len_utf8();
7473 let current_upper = codepoint.is_uppercase();
7474 let next_upper = codepoints
7475 .peek()
7476 .map(|(_, c)| c.is_uppercase())
7477 .unwrap_or(false);
7478
7479 if !current_upper && next_upper {
7480 return Some(&text[start_index..index]);
7481 }
7482 }
7483
7484 index = text.len();
7485 if start_index < text.len() {
7486 return Some(&text[start_index..]);
7487 }
7488 None
7489 })
7490 .flat_map(|word| word.split_inclusive('_'))
7491}
7492
7493trait RangeToAnchorExt {
7494 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
7495}
7496
7497impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7498 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7499 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7500 }
7501}