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