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