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