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