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