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