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