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