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