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 scroll::actions::init(cx);
390
391 workspace::register_project_item::<Editor>(cx);
392 workspace::register_followable_item::<Editor>(cx);
393 workspace::register_deserializable_item::<Editor>(cx);
394}
395
396trait InvalidationRegion {
397 fn ranges(&self) -> &[Range<Anchor>];
398}
399
400#[derive(Clone, Debug, PartialEq)]
401pub enum SelectPhase {
402 Begin {
403 position: DisplayPoint,
404 add: bool,
405 click_count: usize,
406 },
407 BeginColumnar {
408 position: DisplayPoint,
409 goal_column: u32,
410 },
411 Extend {
412 position: DisplayPoint,
413 click_count: usize,
414 },
415 Update {
416 position: DisplayPoint,
417 goal_column: u32,
418 scroll_position: Vector2F,
419 },
420 End,
421}
422
423#[derive(Clone, Debug)]
424pub enum SelectMode {
425 Character,
426 Word(Range<Anchor>),
427 Line(Range<Anchor>),
428 All,
429}
430
431#[derive(Copy, Clone, PartialEq, Eq, Debug)]
432pub enum EditorMode {
433 SingleLine,
434 AutoHeight { max_lines: usize },
435 Full,
436}
437
438#[derive(Clone)]
439pub enum SoftWrap {
440 None,
441 EditorWidth,
442 Column(u32),
443}
444
445#[derive(Clone)]
446pub struct EditorStyle {
447 pub text: TextStyle,
448 pub placeholder_text: Option<TextStyle>,
449 pub theme: theme::Editor,
450 pub theme_id: usize,
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 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
2929 self.clear_copilot_suggestions(cx);
2930 return None;
2931 }
2932
2933 let (buffer, buffer_position) =
2934 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
2935 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
2936 if debounce {
2937 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
2938 }
2939
2940 let completions = copilot
2941 .update(&mut cx, |copilot, cx| {
2942 copilot.completions(&buffer, buffer_position, cx)
2943 })
2944 .await
2945 .log_err()
2946 .into_iter()
2947 .flatten()
2948 .collect_vec();
2949
2950 this.update(&mut cx, |this, cx| {
2951 if !completions.is_empty() {
2952 this.copilot_state.cycled = false;
2953 this.copilot_state.pending_cycling_refresh = Task::ready(None);
2954 this.copilot_state.completions.clear();
2955 this.copilot_state.active_completion_index = 0;
2956 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
2957 for completion in completions {
2958 this.copilot_state.push_completion(completion);
2959 }
2960 this.update_visible_copilot_suggestion(cx);
2961 }
2962 })
2963 .log_err()?;
2964 Some(())
2965 });
2966
2967 Some(())
2968 }
2969
2970 fn cycle_copilot_suggestions(
2971 &mut self,
2972 direction: Direction,
2973 cx: &mut ViewContext<Self>,
2974 ) -> Option<()> {
2975 let copilot = Copilot::global(cx)?;
2976 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
2977 return None;
2978 }
2979
2980 if self.copilot_state.cycled {
2981 self.copilot_state.cycle_completions(direction);
2982 self.update_visible_copilot_suggestion(cx);
2983 } else {
2984 let cursor = self.selections.newest_anchor().head();
2985 let (buffer, buffer_position) =
2986 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
2987 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
2988 let completions = copilot
2989 .update(&mut cx, |copilot, cx| {
2990 copilot.completions_cycling(&buffer, buffer_position, cx)
2991 })
2992 .await;
2993
2994 this.update(&mut cx, |this, cx| {
2995 this.copilot_state.cycled = true;
2996 for completion in completions.log_err().into_iter().flatten() {
2997 this.copilot_state.push_completion(completion);
2998 }
2999 this.copilot_state.cycle_completions(direction);
3000 this.update_visible_copilot_suggestion(cx);
3001 })
3002 .log_err()?;
3003
3004 Some(())
3005 });
3006 }
3007
3008 Some(())
3009 }
3010
3011 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3012 if !self.has_active_copilot_suggestion(cx) {
3013 self.refresh_copilot_suggestions(false, cx);
3014 return;
3015 }
3016
3017 self.update_visible_copilot_suggestion(cx);
3018 }
3019
3020 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3021 if self.has_active_copilot_suggestion(cx) {
3022 self.cycle_copilot_suggestions(Direction::Next, cx);
3023 } else {
3024 self.refresh_copilot_suggestions(false, cx);
3025 }
3026 }
3027
3028 fn previous_copilot_suggestion(
3029 &mut self,
3030 _: &copilot::PreviousSuggestion,
3031 cx: &mut ViewContext<Self>,
3032 ) {
3033 if self.has_active_copilot_suggestion(cx) {
3034 self.cycle_copilot_suggestions(Direction::Prev, cx);
3035 } else {
3036 self.refresh_copilot_suggestions(false, cx);
3037 }
3038 }
3039
3040 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3041 if let Some(suggestion) = self
3042 .display_map
3043 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx))
3044 {
3045 if let Some((copilot, completion)) =
3046 Copilot::global(cx).zip(self.copilot_state.active_completion())
3047 {
3048 copilot
3049 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3050 .detach_and_log_err(cx);
3051 }
3052 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3053 cx.notify();
3054 true
3055 } else {
3056 false
3057 }
3058 }
3059
3060 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3061 if self.has_active_copilot_suggestion(cx) {
3062 if let Some(copilot) = Copilot::global(cx) {
3063 copilot
3064 .update(cx, |copilot, cx| {
3065 copilot.discard_completions(&self.copilot_state.completions, cx)
3066 })
3067 .detach_and_log_err(cx);
3068 }
3069
3070 self.display_map
3071 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
3072 cx.notify();
3073 true
3074 } else {
3075 false
3076 }
3077 }
3078
3079 fn is_copilot_enabled_at(
3080 &self,
3081 location: Anchor,
3082 snapshot: &MultiBufferSnapshot,
3083 cx: &mut ViewContext<Self>,
3084 ) -> bool {
3085 let settings = cx.global::<Settings>();
3086
3087 let language_name = snapshot
3088 .language_at(location)
3089 .map(|language| language.name());
3090 if !settings.show_copilot_suggestions(language_name.as_deref()) {
3091 return false;
3092 }
3093
3094 let file = snapshot.file_at(location);
3095 if let Some(file) = file {
3096 let path = file.path();
3097 if settings
3098 .copilot
3099 .disabled_globs
3100 .iter()
3101 .any(|glob| glob.matches_path(path))
3102 {
3103 return false;
3104 }
3105 }
3106
3107 true
3108 }
3109
3110 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3111 self.display_map.read(cx).has_suggestion()
3112 }
3113
3114 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3115 let snapshot = self.buffer.read(cx).snapshot(cx);
3116 let selection = self.selections.newest_anchor();
3117 let cursor = selection.head();
3118
3119 if self.context_menu.is_some()
3120 || !self.completion_tasks.is_empty()
3121 || selection.start != selection.end
3122 {
3123 self.discard_copilot_suggestion(cx);
3124 } else if let Some(text) = self
3125 .copilot_state
3126 .text_for_active_completion(cursor, &snapshot)
3127 {
3128 self.display_map.update(cx, move |map, cx| {
3129 map.replace_suggestion(
3130 Some(Suggestion {
3131 position: cursor,
3132 text: text.trim_end().into(),
3133 }),
3134 cx,
3135 )
3136 });
3137 cx.notify();
3138 } else {
3139 self.discard_copilot_suggestion(cx);
3140 }
3141 }
3142
3143 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3144 self.copilot_state = Default::default();
3145 self.discard_copilot_suggestion(cx);
3146 }
3147
3148 pub fn render_code_actions_indicator(
3149 &self,
3150 style: &EditorStyle,
3151 active: bool,
3152 cx: &mut ViewContext<Self>,
3153 ) -> Option<AnyElement<Self>> {
3154 if self.available_code_actions.is_some() {
3155 enum CodeActions {}
3156 Some(
3157 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3158 Svg::new("icons/bolt_8.svg")
3159 .with_color(style.code_actions.indicator.style_for(state, active).color)
3160 })
3161 .with_cursor_style(CursorStyle::PointingHand)
3162 .with_padding(Padding::uniform(3.))
3163 .on_down(MouseButton::Left, |_, _, cx| {
3164 cx.dispatch_action(ToggleCodeActions {
3165 deployed_from_indicator: true,
3166 });
3167 })
3168 .into_any(),
3169 )
3170 } else {
3171 None
3172 }
3173 }
3174
3175 pub fn render_fold_indicators(
3176 &self,
3177 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3178 style: &EditorStyle,
3179 gutter_hovered: bool,
3180 line_height: f32,
3181 gutter_margin: f32,
3182 cx: &mut ViewContext<Self>,
3183 ) -> Vec<Option<AnyElement<Self>>> {
3184 enum FoldIndicators {}
3185
3186 let style = style.folds.clone();
3187
3188 fold_data
3189 .iter()
3190 .enumerate()
3191 .map(|(ix, fold_data)| {
3192 fold_data
3193 .map(|(fold_status, buffer_row, active)| {
3194 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3195 MouseEventHandler::<FoldIndicators, _>::new(
3196 ix as usize,
3197 cx,
3198 |mouse_state, _| {
3199 Svg::new(match fold_status {
3200 FoldStatus::Folded => style.folded_icon.clone(),
3201 FoldStatus::Foldable => style.foldable_icon.clone(),
3202 })
3203 .with_color(
3204 style
3205 .indicator
3206 .style_for(
3207 mouse_state,
3208 fold_status == FoldStatus::Folded,
3209 )
3210 .color,
3211 )
3212 .constrained()
3213 .with_width(gutter_margin * style.icon_margin_scale)
3214 .aligned()
3215 .constrained()
3216 .with_height(line_height)
3217 .with_width(gutter_margin)
3218 .aligned()
3219 },
3220 )
3221 .with_cursor_style(CursorStyle::PointingHand)
3222 .with_padding(Padding::uniform(3.))
3223 .on_click(MouseButton::Left, {
3224 move |_, _, cx| {
3225 cx.dispatch_any_action(match fold_status {
3226 FoldStatus::Folded => Box::new(UnfoldAt { buffer_row }),
3227 FoldStatus::Foldable => Box::new(FoldAt { buffer_row }),
3228 });
3229 }
3230 })
3231 .into_any()
3232 })
3233 })
3234 .flatten()
3235 })
3236 .collect()
3237 }
3238
3239 pub fn context_menu_visible(&self) -> bool {
3240 self.context_menu
3241 .as_ref()
3242 .map_or(false, |menu| menu.visible())
3243 }
3244
3245 pub fn render_context_menu(
3246 &self,
3247 cursor_position: DisplayPoint,
3248 style: EditorStyle,
3249 cx: &mut ViewContext<Editor>,
3250 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3251 self.context_menu
3252 .as_ref()
3253 .map(|menu| menu.render(cursor_position, style, cx))
3254 }
3255
3256 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3257 if !matches!(menu, ContextMenu::Completions(_)) {
3258 self.completion_tasks.clear();
3259 }
3260 self.context_menu = Some(menu);
3261 self.discard_copilot_suggestion(cx);
3262 cx.notify();
3263 }
3264
3265 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3266 cx.notify();
3267 self.completion_tasks.clear();
3268 let context_menu = self.context_menu.take();
3269 if context_menu.is_some() {
3270 self.update_visible_copilot_suggestion(cx);
3271 }
3272 context_menu
3273 }
3274
3275 pub fn insert_snippet(
3276 &mut self,
3277 insertion_ranges: &[Range<usize>],
3278 snippet: Snippet,
3279 cx: &mut ViewContext<Self>,
3280 ) -> Result<()> {
3281 let tabstops = self.buffer.update(cx, |buffer, cx| {
3282 let snippet_text: Arc<str> = snippet.text.clone().into();
3283 buffer.edit(
3284 insertion_ranges
3285 .iter()
3286 .cloned()
3287 .map(|range| (range, snippet_text.clone())),
3288 Some(AutoindentMode::EachLine),
3289 cx,
3290 );
3291
3292 let snapshot = &*buffer.read(cx);
3293 let snippet = &snippet;
3294 snippet
3295 .tabstops
3296 .iter()
3297 .map(|tabstop| {
3298 let mut tabstop_ranges = tabstop
3299 .iter()
3300 .flat_map(|tabstop_range| {
3301 let mut delta = 0_isize;
3302 insertion_ranges.iter().map(move |insertion_range| {
3303 let insertion_start = insertion_range.start as isize + delta;
3304 delta +=
3305 snippet.text.len() as isize - insertion_range.len() as isize;
3306
3307 let start = snapshot.anchor_before(
3308 (insertion_start + tabstop_range.start) as usize,
3309 );
3310 let end = snapshot
3311 .anchor_after((insertion_start + tabstop_range.end) as usize);
3312 start..end
3313 })
3314 })
3315 .collect::<Vec<_>>();
3316 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3317 tabstop_ranges
3318 })
3319 .collect::<Vec<_>>()
3320 });
3321
3322 if let Some(tabstop) = tabstops.first() {
3323 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3324 s.select_ranges(tabstop.iter().cloned());
3325 });
3326 self.snippet_stack.push(SnippetState {
3327 active_index: 0,
3328 ranges: tabstops,
3329 });
3330 }
3331
3332 Ok(())
3333 }
3334
3335 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3336 self.move_to_snippet_tabstop(Bias::Right, cx)
3337 }
3338
3339 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3340 self.move_to_snippet_tabstop(Bias::Left, cx)
3341 }
3342
3343 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3344 if let Some(mut snippet) = self.snippet_stack.pop() {
3345 match bias {
3346 Bias::Left => {
3347 if snippet.active_index > 0 {
3348 snippet.active_index -= 1;
3349 } else {
3350 self.snippet_stack.push(snippet);
3351 return false;
3352 }
3353 }
3354 Bias::Right => {
3355 if snippet.active_index + 1 < snippet.ranges.len() {
3356 snippet.active_index += 1;
3357 } else {
3358 self.snippet_stack.push(snippet);
3359 return false;
3360 }
3361 }
3362 }
3363 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3364 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3365 s.select_anchor_ranges(current_ranges.iter().cloned())
3366 });
3367 // If snippet state is not at the last tabstop, push it back on the stack
3368 if snippet.active_index + 1 < snippet.ranges.len() {
3369 self.snippet_stack.push(snippet);
3370 }
3371 return true;
3372 }
3373 }
3374
3375 false
3376 }
3377
3378 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3379 self.transact(cx, |this, cx| {
3380 this.select_all(&SelectAll, cx);
3381 this.insert("", cx);
3382 });
3383 }
3384
3385 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3386 self.transact(cx, |this, cx| {
3387 this.select_autoclose_pair(cx);
3388 let mut selections = this.selections.all::<Point>(cx);
3389 if !this.selections.line_mode {
3390 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3391 for selection in &mut selections {
3392 if selection.is_empty() {
3393 let old_head = selection.head();
3394 let mut new_head =
3395 movement::left(&display_map, old_head.to_display_point(&display_map))
3396 .to_point(&display_map);
3397 if let Some((buffer, line_buffer_range)) = display_map
3398 .buffer_snapshot
3399 .buffer_line_for_row(old_head.row)
3400 {
3401 let indent_size =
3402 buffer.indent_size_for_line(line_buffer_range.start.row);
3403 let language_name = buffer
3404 .language_at(line_buffer_range.start)
3405 .map(|language| language.name());
3406 let indent_len = match indent_size.kind {
3407 IndentKind::Space => {
3408 cx.global::<Settings>().tab_size(language_name.as_deref())
3409 }
3410 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3411 };
3412 if old_head.column <= indent_size.len && old_head.column > 0 {
3413 let indent_len = indent_len.get();
3414 new_head = cmp::min(
3415 new_head,
3416 Point::new(
3417 old_head.row,
3418 ((old_head.column - 1) / indent_len) * indent_len,
3419 ),
3420 );
3421 }
3422 }
3423
3424 selection.set_head(new_head, SelectionGoal::None);
3425 }
3426 }
3427 }
3428
3429 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3430 this.insert("", cx);
3431 this.refresh_copilot_suggestions(true, cx);
3432 });
3433 }
3434
3435 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3436 self.transact(cx, |this, cx| {
3437 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3438 let line_mode = s.line_mode;
3439 s.move_with(|map, selection| {
3440 if selection.is_empty() && !line_mode {
3441 let cursor = movement::right(map, selection.head());
3442 selection.set_head(cursor, SelectionGoal::None);
3443 }
3444 })
3445 });
3446 this.insert("", cx);
3447 this.refresh_copilot_suggestions(true, cx);
3448 });
3449 }
3450
3451 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3452 if self.move_to_prev_snippet_tabstop(cx) {
3453 return;
3454 }
3455
3456 self.outdent(&Outdent, cx);
3457 }
3458
3459 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3460 if self.move_to_next_snippet_tabstop(cx) {
3461 return;
3462 }
3463
3464 let mut selections = self.selections.all_adjusted(cx);
3465 let buffer = self.buffer.read(cx);
3466 let snapshot = buffer.snapshot(cx);
3467 let rows_iter = selections.iter().map(|s| s.head().row);
3468 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3469
3470 let mut edits = Vec::new();
3471 let mut prev_edited_row = 0;
3472 let mut row_delta = 0;
3473 for selection in &mut selections {
3474 if selection.start.row != prev_edited_row {
3475 row_delta = 0;
3476 }
3477 prev_edited_row = selection.end.row;
3478
3479 // If the selection is non-empty, then increase the indentation of the selected lines.
3480 if !selection.is_empty() {
3481 row_delta =
3482 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3483 continue;
3484 }
3485
3486 // If the selection is empty and the cursor is in the leading whitespace before the
3487 // suggested indentation, then auto-indent the line.
3488 let cursor = selection.head();
3489 let current_indent = snapshot.indent_size_for_line(cursor.row);
3490 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3491 if cursor.column < suggested_indent.len
3492 && cursor.column <= current_indent.len
3493 && current_indent.len <= suggested_indent.len
3494 {
3495 selection.start = Point::new(cursor.row, suggested_indent.len);
3496 selection.end = selection.start;
3497 if row_delta == 0 {
3498 edits.extend(Buffer::edit_for_indent_size_adjustment(
3499 cursor.row,
3500 current_indent,
3501 suggested_indent,
3502 ));
3503 row_delta = suggested_indent.len - current_indent.len;
3504 }
3505 continue;
3506 }
3507 }
3508
3509 // Accept copilot suggestion if there is only one selection and the cursor is not
3510 // in the leading whitespace.
3511 if self.selections.count() == 1
3512 && cursor.column >= current_indent.len
3513 && self.has_active_copilot_suggestion(cx)
3514 {
3515 self.accept_copilot_suggestion(cx);
3516 return;
3517 }
3518
3519 // Otherwise, insert a hard or soft tab.
3520 let settings = cx.global::<Settings>();
3521 let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
3522 let tab_size = if settings.hard_tabs(language_name.as_deref()) {
3523 IndentSize::tab()
3524 } else {
3525 let tab_size = settings.tab_size(language_name.as_deref()).get();
3526 let char_column = snapshot
3527 .text_for_range(Point::new(cursor.row, 0)..cursor)
3528 .flat_map(str::chars)
3529 .count()
3530 + row_delta as usize;
3531 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3532 IndentSize::spaces(chars_to_next_tab_stop)
3533 };
3534 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3535 selection.end = selection.start;
3536 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3537 row_delta += tab_size.len;
3538 }
3539
3540 self.transact(cx, |this, cx| {
3541 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3542 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3543 this.refresh_copilot_suggestions(true, cx);
3544 });
3545 }
3546
3547 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3548 let mut selections = self.selections.all::<Point>(cx);
3549 let mut prev_edited_row = 0;
3550 let mut row_delta = 0;
3551 let mut edits = Vec::new();
3552 let buffer = self.buffer.read(cx);
3553 let snapshot = buffer.snapshot(cx);
3554 for selection in &mut selections {
3555 if selection.start.row != prev_edited_row {
3556 row_delta = 0;
3557 }
3558 prev_edited_row = selection.end.row;
3559
3560 row_delta =
3561 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3562 }
3563
3564 self.transact(cx, |this, cx| {
3565 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3566 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3567 });
3568 }
3569
3570 fn indent_selection(
3571 buffer: &MultiBuffer,
3572 snapshot: &MultiBufferSnapshot,
3573 selection: &mut Selection<Point>,
3574 edits: &mut Vec<(Range<Point>, String)>,
3575 delta_for_start_row: u32,
3576 cx: &AppContext,
3577 ) -> u32 {
3578 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3579 let settings = cx.global::<Settings>();
3580 let tab_size = settings.tab_size(language_name.as_deref()).get();
3581 let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
3582 IndentKind::Tab
3583 } else {
3584 IndentKind::Space
3585 };
3586 let mut start_row = selection.start.row;
3587 let mut end_row = selection.end.row + 1;
3588
3589 // If a selection ends at the beginning of a line, don't indent
3590 // that last line.
3591 if selection.end.column == 0 {
3592 end_row -= 1;
3593 }
3594
3595 // Avoid re-indenting a row that has already been indented by a
3596 // previous selection, but still update this selection's column
3597 // to reflect that indentation.
3598 if delta_for_start_row > 0 {
3599 start_row += 1;
3600 selection.start.column += delta_for_start_row;
3601 if selection.end.row == selection.start.row {
3602 selection.end.column += delta_for_start_row;
3603 }
3604 }
3605
3606 let mut delta_for_end_row = 0;
3607 for row in start_row..end_row {
3608 let current_indent = snapshot.indent_size_for_line(row);
3609 let indent_delta = match (current_indent.kind, indent_kind) {
3610 (IndentKind::Space, IndentKind::Space) => {
3611 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3612 IndentSize::spaces(columns_to_next_tab_stop)
3613 }
3614 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3615 (_, IndentKind::Tab) => IndentSize::tab(),
3616 };
3617
3618 let row_start = Point::new(row, 0);
3619 edits.push((
3620 row_start..row_start,
3621 indent_delta.chars().collect::<String>(),
3622 ));
3623
3624 // Update this selection's endpoints to reflect the indentation.
3625 if row == selection.start.row {
3626 selection.start.column += indent_delta.len;
3627 }
3628 if row == selection.end.row {
3629 selection.end.column += indent_delta.len;
3630 delta_for_end_row = indent_delta.len;
3631 }
3632 }
3633
3634 if selection.start.row == selection.end.row {
3635 delta_for_start_row + delta_for_end_row
3636 } else {
3637 delta_for_end_row
3638 }
3639 }
3640
3641 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3642 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3643 let selections = self.selections.all::<Point>(cx);
3644 let mut deletion_ranges = Vec::new();
3645 let mut last_outdent = None;
3646 {
3647 let buffer = self.buffer.read(cx);
3648 let snapshot = buffer.snapshot(cx);
3649 for selection in &selections {
3650 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
3651 let tab_size = cx
3652 .global::<Settings>()
3653 .tab_size(language_name.as_deref())
3654 .get();
3655 let mut rows = selection.spanned_rows(false, &display_map);
3656
3657 // Avoid re-outdenting a row that has already been outdented by a
3658 // previous selection.
3659 if let Some(last_row) = last_outdent {
3660 if last_row == rows.start {
3661 rows.start += 1;
3662 }
3663 }
3664
3665 for row in rows {
3666 let indent_size = snapshot.indent_size_for_line(row);
3667 if indent_size.len > 0 {
3668 let deletion_len = match indent_size.kind {
3669 IndentKind::Space => {
3670 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3671 if columns_to_prev_tab_stop == 0 {
3672 tab_size
3673 } else {
3674 columns_to_prev_tab_stop
3675 }
3676 }
3677 IndentKind::Tab => 1,
3678 };
3679 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3680 last_outdent = Some(row);
3681 }
3682 }
3683 }
3684 }
3685
3686 self.transact(cx, |this, cx| {
3687 this.buffer.update(cx, |buffer, cx| {
3688 let empty_str: Arc<str> = "".into();
3689 buffer.edit(
3690 deletion_ranges
3691 .into_iter()
3692 .map(|range| (range, empty_str.clone())),
3693 None,
3694 cx,
3695 );
3696 });
3697 let selections = this.selections.all::<usize>(cx);
3698 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3699 });
3700 }
3701
3702 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3703 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3704 let selections = self.selections.all::<Point>(cx);
3705
3706 let mut new_cursors = Vec::new();
3707 let mut edit_ranges = Vec::new();
3708 let mut selections = selections.iter().peekable();
3709 while let Some(selection) = selections.next() {
3710 let mut rows = selection.spanned_rows(false, &display_map);
3711 let goal_display_column = selection.head().to_display_point(&display_map).column();
3712
3713 // Accumulate contiguous regions of rows that we want to delete.
3714 while let Some(next_selection) = selections.peek() {
3715 let next_rows = next_selection.spanned_rows(false, &display_map);
3716 if next_rows.start <= rows.end {
3717 rows.end = next_rows.end;
3718 selections.next().unwrap();
3719 } else {
3720 break;
3721 }
3722 }
3723
3724 let buffer = &display_map.buffer_snapshot;
3725 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3726 let edit_end;
3727 let cursor_buffer_row;
3728 if buffer.max_point().row >= rows.end {
3729 // If there's a line after the range, delete the \n from the end of the row range
3730 // and position the cursor on the next line.
3731 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3732 cursor_buffer_row = rows.end;
3733 } else {
3734 // If there isn't a line after the range, delete the \n from the line before the
3735 // start of the row range and position the cursor there.
3736 edit_start = edit_start.saturating_sub(1);
3737 edit_end = buffer.len();
3738 cursor_buffer_row = rows.start.saturating_sub(1);
3739 }
3740
3741 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3742 *cursor.column_mut() =
3743 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3744
3745 new_cursors.push((
3746 selection.id,
3747 buffer.anchor_after(cursor.to_point(&display_map)),
3748 ));
3749 edit_ranges.push(edit_start..edit_end);
3750 }
3751
3752 self.transact(cx, |this, cx| {
3753 let buffer = this.buffer.update(cx, |buffer, cx| {
3754 let empty_str: Arc<str> = "".into();
3755 buffer.edit(
3756 edit_ranges
3757 .into_iter()
3758 .map(|range| (range, empty_str.clone())),
3759 None,
3760 cx,
3761 );
3762 buffer.snapshot(cx)
3763 });
3764 let new_selections = new_cursors
3765 .into_iter()
3766 .map(|(id, cursor)| {
3767 let cursor = cursor.to_point(&buffer);
3768 Selection {
3769 id,
3770 start: cursor,
3771 end: cursor,
3772 reversed: false,
3773 goal: SelectionGoal::None,
3774 }
3775 })
3776 .collect();
3777
3778 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3779 s.select(new_selections);
3780 });
3781 });
3782 }
3783
3784 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3785 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3786 let buffer = &display_map.buffer_snapshot;
3787 let selections = self.selections.all::<Point>(cx);
3788
3789 let mut edits = Vec::new();
3790 let mut selections_iter = selections.iter().peekable();
3791 while let Some(selection) = selections_iter.next() {
3792 // Avoid duplicating the same lines twice.
3793 let mut rows = selection.spanned_rows(false, &display_map);
3794
3795 while let Some(next_selection) = selections_iter.peek() {
3796 let next_rows = next_selection.spanned_rows(false, &display_map);
3797 if next_rows.start < rows.end {
3798 rows.end = next_rows.end;
3799 selections_iter.next().unwrap();
3800 } else {
3801 break;
3802 }
3803 }
3804
3805 // Copy the text from the selected row region and splice it at the start of the region.
3806 let start = Point::new(rows.start, 0);
3807 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3808 let text = buffer
3809 .text_for_range(start..end)
3810 .chain(Some("\n"))
3811 .collect::<String>();
3812 edits.push((start..start, text));
3813 }
3814
3815 self.transact(cx, |this, cx| {
3816 this.buffer.update(cx, |buffer, cx| {
3817 buffer.edit(edits, None, cx);
3818 });
3819
3820 this.request_autoscroll(Autoscroll::fit(), cx);
3821 });
3822 }
3823
3824 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3825 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3826 let buffer = self.buffer.read(cx).snapshot(cx);
3827
3828 let mut edits = Vec::new();
3829 let mut unfold_ranges = Vec::new();
3830 let mut refold_ranges = Vec::new();
3831
3832 let selections = self.selections.all::<Point>(cx);
3833 let mut selections = selections.iter().peekable();
3834 let mut contiguous_row_selections = Vec::new();
3835 let mut new_selections = Vec::new();
3836
3837 while let Some(selection) = selections.next() {
3838 // Find all the selections that span a contiguous row range
3839 let (start_row, end_row) = consume_contiguous_rows(
3840 &mut contiguous_row_selections,
3841 selection,
3842 &display_map,
3843 &mut selections,
3844 );
3845
3846 // Move the text spanned by the row range to be before the line preceding the row range
3847 if start_row > 0 {
3848 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3849 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3850 let insertion_point = display_map
3851 .prev_line_boundary(Point::new(start_row - 1, 0))
3852 .0;
3853
3854 // Don't move lines across excerpts
3855 if buffer
3856 .excerpt_boundaries_in_range((
3857 Bound::Excluded(insertion_point),
3858 Bound::Included(range_to_move.end),
3859 ))
3860 .next()
3861 .is_none()
3862 {
3863 let text = buffer
3864 .text_for_range(range_to_move.clone())
3865 .flat_map(|s| s.chars())
3866 .skip(1)
3867 .chain(['\n'])
3868 .collect::<String>();
3869
3870 edits.push((
3871 buffer.anchor_after(range_to_move.start)
3872 ..buffer.anchor_before(range_to_move.end),
3873 String::new(),
3874 ));
3875 let insertion_anchor = buffer.anchor_after(insertion_point);
3876 edits.push((insertion_anchor..insertion_anchor, text));
3877
3878 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3879
3880 // Move selections up
3881 new_selections.extend(contiguous_row_selections.drain(..).map(
3882 |mut selection| {
3883 selection.start.row -= row_delta;
3884 selection.end.row -= row_delta;
3885 selection
3886 },
3887 ));
3888
3889 // Move folds up
3890 unfold_ranges.push(range_to_move.clone());
3891 for fold in display_map.folds_in_range(
3892 buffer.anchor_before(range_to_move.start)
3893 ..buffer.anchor_after(range_to_move.end),
3894 ) {
3895 let mut start = fold.start.to_point(&buffer);
3896 let mut end = fold.end.to_point(&buffer);
3897 start.row -= row_delta;
3898 end.row -= row_delta;
3899 refold_ranges.push(start..end);
3900 }
3901 }
3902 }
3903
3904 // If we didn't move line(s), preserve the existing selections
3905 new_selections.append(&mut contiguous_row_selections);
3906 }
3907
3908 self.transact(cx, |this, cx| {
3909 this.unfold_ranges(unfold_ranges, true, true, cx);
3910 this.buffer.update(cx, |buffer, cx| {
3911 for (range, text) in edits {
3912 buffer.edit([(range, text)], None, cx);
3913 }
3914 });
3915 this.fold_ranges(refold_ranges, true, cx);
3916 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3917 s.select(new_selections);
3918 })
3919 });
3920 }
3921
3922 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3923 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3924 let buffer = self.buffer.read(cx).snapshot(cx);
3925
3926 let mut edits = Vec::new();
3927 let mut unfold_ranges = Vec::new();
3928 let mut refold_ranges = Vec::new();
3929
3930 let selections = self.selections.all::<Point>(cx);
3931 let mut selections = selections.iter().peekable();
3932 let mut contiguous_row_selections = Vec::new();
3933 let mut new_selections = Vec::new();
3934
3935 while let Some(selection) = selections.next() {
3936 // Find all the selections that span a contiguous row range
3937 let (start_row, end_row) = consume_contiguous_rows(
3938 &mut contiguous_row_selections,
3939 selection,
3940 &display_map,
3941 &mut selections,
3942 );
3943
3944 // Move the text spanned by the row range to be after the last line of the row range
3945 if end_row <= buffer.max_point().row {
3946 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3947 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3948
3949 // Don't move lines across excerpt boundaries
3950 if buffer
3951 .excerpt_boundaries_in_range((
3952 Bound::Excluded(range_to_move.start),
3953 Bound::Included(insertion_point),
3954 ))
3955 .next()
3956 .is_none()
3957 {
3958 let mut text = String::from("\n");
3959 text.extend(buffer.text_for_range(range_to_move.clone()));
3960 text.pop(); // Drop trailing newline
3961 edits.push((
3962 buffer.anchor_after(range_to_move.start)
3963 ..buffer.anchor_before(range_to_move.end),
3964 String::new(),
3965 ));
3966 let insertion_anchor = buffer.anchor_after(insertion_point);
3967 edits.push((insertion_anchor..insertion_anchor, text));
3968
3969 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3970
3971 // Move selections down
3972 new_selections.extend(contiguous_row_selections.drain(..).map(
3973 |mut selection| {
3974 selection.start.row += row_delta;
3975 selection.end.row += row_delta;
3976 selection
3977 },
3978 ));
3979
3980 // Move folds down
3981 unfold_ranges.push(range_to_move.clone());
3982 for fold in display_map.folds_in_range(
3983 buffer.anchor_before(range_to_move.start)
3984 ..buffer.anchor_after(range_to_move.end),
3985 ) {
3986 let mut start = fold.start.to_point(&buffer);
3987 let mut end = fold.end.to_point(&buffer);
3988 start.row += row_delta;
3989 end.row += row_delta;
3990 refold_ranges.push(start..end);
3991 }
3992 }
3993 }
3994
3995 // If we didn't move line(s), preserve the existing selections
3996 new_selections.append(&mut contiguous_row_selections);
3997 }
3998
3999 self.transact(cx, |this, cx| {
4000 this.unfold_ranges(unfold_ranges, true, true, cx);
4001 this.buffer.update(cx, |buffer, cx| {
4002 for (range, text) in edits {
4003 buffer.edit([(range, text)], None, cx);
4004 }
4005 });
4006 this.fold_ranges(refold_ranges, true, cx);
4007 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4008 });
4009 }
4010
4011 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4012 self.transact(cx, |this, cx| {
4013 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4014 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4015 let line_mode = s.line_mode;
4016 s.move_with(|display_map, selection| {
4017 if !selection.is_empty() || line_mode {
4018 return;
4019 }
4020
4021 let mut head = selection.head();
4022 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4023 if head.column() == display_map.line_len(head.row()) {
4024 transpose_offset = display_map
4025 .buffer_snapshot
4026 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4027 }
4028
4029 if transpose_offset == 0 {
4030 return;
4031 }
4032
4033 *head.column_mut() += 1;
4034 head = display_map.clip_point(head, Bias::Right);
4035 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4036
4037 let transpose_start = display_map
4038 .buffer_snapshot
4039 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4040 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4041 let transpose_end = display_map
4042 .buffer_snapshot
4043 .clip_offset(transpose_offset + 1, Bias::Right);
4044 if let Some(ch) =
4045 display_map.buffer_snapshot.chars_at(transpose_start).next()
4046 {
4047 edits.push((transpose_start..transpose_offset, String::new()));
4048 edits.push((transpose_end..transpose_end, ch.to_string()));
4049 }
4050 }
4051 });
4052 edits
4053 });
4054 this.buffer
4055 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4056 let selections = this.selections.all::<usize>(cx);
4057 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4058 s.select(selections);
4059 });
4060 });
4061 }
4062
4063 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4064 let mut text = String::new();
4065 let buffer = self.buffer.read(cx).snapshot(cx);
4066 let mut selections = self.selections.all::<Point>(cx);
4067 let mut clipboard_selections = Vec::with_capacity(selections.len());
4068 {
4069 let max_point = buffer.max_point();
4070 for selection in &mut selections {
4071 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4072 if is_entire_line {
4073 selection.start = Point::new(selection.start.row, 0);
4074 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4075 selection.goal = SelectionGoal::None;
4076 }
4077 let mut len = 0;
4078 for chunk in buffer.text_for_range(selection.start..selection.end) {
4079 text.push_str(chunk);
4080 len += chunk.len();
4081 }
4082 clipboard_selections.push(ClipboardSelection {
4083 len,
4084 is_entire_line,
4085 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4086 });
4087 }
4088 }
4089
4090 self.transact(cx, |this, cx| {
4091 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4092 s.select(selections);
4093 });
4094 this.insert("", cx);
4095 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4096 });
4097 }
4098
4099 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4100 let selections = self.selections.all::<Point>(cx);
4101 let buffer = self.buffer.read(cx).read(cx);
4102 let mut text = String::new();
4103
4104 let mut clipboard_selections = Vec::with_capacity(selections.len());
4105 {
4106 let max_point = buffer.max_point();
4107 for selection in selections.iter() {
4108 let mut start = selection.start;
4109 let mut end = selection.end;
4110 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4111 if is_entire_line {
4112 start = Point::new(start.row, 0);
4113 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4114 }
4115 let mut len = 0;
4116 for chunk in buffer.text_for_range(start..end) {
4117 text.push_str(chunk);
4118 len += chunk.len();
4119 }
4120 clipboard_selections.push(ClipboardSelection {
4121 len,
4122 is_entire_line,
4123 first_line_indent: buffer.indent_size_for_line(start.row).len,
4124 });
4125 }
4126 }
4127
4128 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4129 }
4130
4131 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4132 self.transact(cx, |this, cx| {
4133 if let Some(item) = cx.read_from_clipboard() {
4134 let mut clipboard_text = Cow::Borrowed(item.text());
4135 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4136 let old_selections = this.selections.all::<usize>(cx);
4137 let all_selections_were_entire_line =
4138 clipboard_selections.iter().all(|s| s.is_entire_line);
4139 let first_selection_indent_column =
4140 clipboard_selections.first().map(|s| s.first_line_indent);
4141 if clipboard_selections.len() != old_selections.len() {
4142 let mut newline_separated_text = String::new();
4143 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4144 let mut ix = 0;
4145 while let Some(clipboard_selection) = clipboard_selections.next() {
4146 newline_separated_text
4147 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4148 ix += clipboard_selection.len;
4149 if clipboard_selections.peek().is_some() {
4150 newline_separated_text.push('\n');
4151 }
4152 }
4153 clipboard_text = Cow::Owned(newline_separated_text);
4154 }
4155
4156 this.buffer.update(cx, |buffer, cx| {
4157 let snapshot = buffer.read(cx);
4158 let mut start_offset = 0;
4159 let mut edits = Vec::new();
4160 let mut original_indent_columns = Vec::new();
4161 let line_mode = this.selections.line_mode;
4162 for (ix, selection) in old_selections.iter().enumerate() {
4163 let to_insert;
4164 let entire_line;
4165 let original_indent_column;
4166 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4167 let end_offset = start_offset + clipboard_selection.len;
4168 to_insert = &clipboard_text[start_offset..end_offset];
4169 entire_line = clipboard_selection.is_entire_line;
4170 start_offset = end_offset;
4171 original_indent_column =
4172 Some(clipboard_selection.first_line_indent);
4173 } else {
4174 to_insert = clipboard_text.as_str();
4175 entire_line = all_selections_were_entire_line;
4176 original_indent_column = first_selection_indent_column
4177 }
4178
4179 // If the corresponding selection was empty when this slice of the
4180 // clipboard text was written, then the entire line containing the
4181 // selection was copied. If this selection is also currently empty,
4182 // then paste the line before the current line of the buffer.
4183 let range = if selection.is_empty() && !line_mode && entire_line {
4184 let column = selection.start.to_point(&snapshot).column as usize;
4185 let line_start = selection.start - column;
4186 line_start..line_start
4187 } else {
4188 selection.range()
4189 };
4190
4191 edits.push((range, to_insert));
4192 original_indent_columns.extend(original_indent_column);
4193 }
4194 drop(snapshot);
4195
4196 buffer.edit(
4197 edits,
4198 Some(AutoindentMode::Block {
4199 original_indent_columns,
4200 }),
4201 cx,
4202 );
4203 });
4204
4205 let selections = this.selections.all::<usize>(cx);
4206 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4207 } else {
4208 this.insert(&clipboard_text, cx);
4209 }
4210 }
4211 });
4212 }
4213
4214 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4215 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4216 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4217 self.change_selections(None, cx, |s| {
4218 s.select_anchors(selections.to_vec());
4219 });
4220 }
4221 self.request_autoscroll(Autoscroll::fit(), cx);
4222 self.unmark_text(cx);
4223 self.refresh_copilot_suggestions(true, cx);
4224 cx.emit(Event::Edited);
4225 }
4226 }
4227
4228 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4229 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4230 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4231 {
4232 self.change_selections(None, cx, |s| {
4233 s.select_anchors(selections.to_vec());
4234 });
4235 }
4236 self.request_autoscroll(Autoscroll::fit(), cx);
4237 self.unmark_text(cx);
4238 self.refresh_copilot_suggestions(true, cx);
4239 cx.emit(Event::Edited);
4240 }
4241 }
4242
4243 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4244 self.buffer
4245 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4246 }
4247
4248 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4249 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4250 let line_mode = s.line_mode;
4251 s.move_with(|map, selection| {
4252 let cursor = if selection.is_empty() && !line_mode {
4253 movement::left(map, selection.start)
4254 } else {
4255 selection.start
4256 };
4257 selection.collapse_to(cursor, SelectionGoal::None);
4258 });
4259 })
4260 }
4261
4262 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4263 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4264 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4265 })
4266 }
4267
4268 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4269 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4270 let line_mode = s.line_mode;
4271 s.move_with(|map, selection| {
4272 let cursor = if selection.is_empty() && !line_mode {
4273 movement::right(map, selection.end)
4274 } else {
4275 selection.end
4276 };
4277 selection.collapse_to(cursor, SelectionGoal::None)
4278 });
4279 })
4280 }
4281
4282 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4283 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4284 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4285 })
4286 }
4287
4288 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4289 if self.take_rename(true, cx).is_some() {
4290 return;
4291 }
4292
4293 if let Some(context_menu) = self.context_menu.as_mut() {
4294 if context_menu.select_prev(cx) {
4295 return;
4296 }
4297 }
4298
4299 if matches!(self.mode, EditorMode::SingleLine) {
4300 cx.propagate_action();
4301 return;
4302 }
4303
4304 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4305 let line_mode = s.line_mode;
4306 s.move_with(|map, selection| {
4307 if !selection.is_empty() && !line_mode {
4308 selection.goal = SelectionGoal::None;
4309 }
4310 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4311 selection.collapse_to(cursor, goal);
4312 });
4313 })
4314 }
4315
4316 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4317 if self.take_rename(true, cx).is_some() {
4318 return;
4319 }
4320
4321 if self
4322 .context_menu
4323 .as_mut()
4324 .map(|menu| menu.select_first(cx))
4325 .unwrap_or(false)
4326 {
4327 return;
4328 }
4329
4330 if matches!(self.mode, EditorMode::SingleLine) {
4331 cx.propagate_action();
4332 return;
4333 }
4334
4335 let row_count = if let Some(row_count) = self.visible_line_count() {
4336 row_count as u32 - 1
4337 } else {
4338 return;
4339 };
4340
4341 let autoscroll = if action.center_cursor {
4342 Autoscroll::center()
4343 } else {
4344 Autoscroll::fit()
4345 };
4346
4347 self.change_selections(Some(autoscroll), cx, |s| {
4348 let line_mode = s.line_mode;
4349 s.move_with(|map, selection| {
4350 if !selection.is_empty() && !line_mode {
4351 selection.goal = SelectionGoal::None;
4352 }
4353 let (cursor, goal) =
4354 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4355 selection.collapse_to(cursor, goal);
4356 });
4357 });
4358 }
4359
4360 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4361 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4362 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4363 })
4364 }
4365
4366 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4367 self.take_rename(true, cx);
4368
4369 if let Some(context_menu) = self.context_menu.as_mut() {
4370 if context_menu.select_next(cx) {
4371 return;
4372 }
4373 }
4374
4375 if self.mode == EditorMode::SingleLine {
4376 cx.propagate_action();
4377 return;
4378 }
4379
4380 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4381 let line_mode = s.line_mode;
4382 s.move_with(|map, selection| {
4383 if !selection.is_empty() && !line_mode {
4384 selection.goal = SelectionGoal::None;
4385 }
4386 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4387 selection.collapse_to(cursor, goal);
4388 });
4389 });
4390 }
4391
4392 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4393 if self.take_rename(true, cx).is_some() {
4394 return;
4395 }
4396
4397 if self
4398 .context_menu
4399 .as_mut()
4400 .map(|menu| menu.select_last(cx))
4401 .unwrap_or(false)
4402 {
4403 return;
4404 }
4405
4406 if matches!(self.mode, EditorMode::SingleLine) {
4407 cx.propagate_action();
4408 return;
4409 }
4410
4411 let row_count = if let Some(row_count) = self.visible_line_count() {
4412 row_count as u32 - 1
4413 } else {
4414 return;
4415 };
4416
4417 let autoscroll = if action.center_cursor {
4418 Autoscroll::center()
4419 } else {
4420 Autoscroll::fit()
4421 };
4422
4423 self.change_selections(Some(autoscroll), cx, |s| {
4424 let line_mode = s.line_mode;
4425 s.move_with(|map, selection| {
4426 if !selection.is_empty() && !line_mode {
4427 selection.goal = SelectionGoal::None;
4428 }
4429 let (cursor, goal) =
4430 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4431 selection.collapse_to(cursor, goal);
4432 });
4433 });
4434 }
4435
4436 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4437 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4438 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4439 });
4440 }
4441
4442 pub fn move_to_previous_word_start(
4443 &mut self,
4444 _: &MoveToPreviousWordStart,
4445 cx: &mut ViewContext<Self>,
4446 ) {
4447 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4448 s.move_cursors_with(|map, head, _| {
4449 (
4450 movement::previous_word_start(map, head),
4451 SelectionGoal::None,
4452 )
4453 });
4454 })
4455 }
4456
4457 pub fn move_to_previous_subword_start(
4458 &mut self,
4459 _: &MoveToPreviousSubwordStart,
4460 cx: &mut ViewContext<Self>,
4461 ) {
4462 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4463 s.move_cursors_with(|map, head, _| {
4464 (
4465 movement::previous_subword_start(map, head),
4466 SelectionGoal::None,
4467 )
4468 });
4469 })
4470 }
4471
4472 pub fn select_to_previous_word_start(
4473 &mut self,
4474 _: &SelectToPreviousWordStart,
4475 cx: &mut ViewContext<Self>,
4476 ) {
4477 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4478 s.move_heads_with(|map, head, _| {
4479 (
4480 movement::previous_word_start(map, head),
4481 SelectionGoal::None,
4482 )
4483 });
4484 })
4485 }
4486
4487 pub fn select_to_previous_subword_start(
4488 &mut self,
4489 _: &SelectToPreviousSubwordStart,
4490 cx: &mut ViewContext<Self>,
4491 ) {
4492 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4493 s.move_heads_with(|map, head, _| {
4494 (
4495 movement::previous_subword_start(map, head),
4496 SelectionGoal::None,
4497 )
4498 });
4499 })
4500 }
4501
4502 pub fn delete_to_previous_word_start(
4503 &mut self,
4504 _: &DeleteToPreviousWordStart,
4505 cx: &mut ViewContext<Self>,
4506 ) {
4507 self.transact(cx, |this, cx| {
4508 this.select_autoclose_pair(cx);
4509 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4510 let line_mode = s.line_mode;
4511 s.move_with(|map, selection| {
4512 if selection.is_empty() && !line_mode {
4513 let cursor = movement::previous_word_start(map, selection.head());
4514 selection.set_head(cursor, SelectionGoal::None);
4515 }
4516 });
4517 });
4518 this.insert("", cx);
4519 });
4520 }
4521
4522 pub fn delete_to_previous_subword_start(
4523 &mut self,
4524 _: &DeleteToPreviousSubwordStart,
4525 cx: &mut ViewContext<Self>,
4526 ) {
4527 self.transact(cx, |this, cx| {
4528 this.select_autoclose_pair(cx);
4529 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4530 let line_mode = s.line_mode;
4531 s.move_with(|map, selection| {
4532 if selection.is_empty() && !line_mode {
4533 let cursor = movement::previous_subword_start(map, selection.head());
4534 selection.set_head(cursor, SelectionGoal::None);
4535 }
4536 });
4537 });
4538 this.insert("", cx);
4539 });
4540 }
4541
4542 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4543 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4544 s.move_cursors_with(|map, head, _| {
4545 (movement::next_word_end(map, head), SelectionGoal::None)
4546 });
4547 })
4548 }
4549
4550 pub fn move_to_next_subword_end(
4551 &mut self,
4552 _: &MoveToNextSubwordEnd,
4553 cx: &mut ViewContext<Self>,
4554 ) {
4555 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4556 s.move_cursors_with(|map, head, _| {
4557 (movement::next_subword_end(map, head), SelectionGoal::None)
4558 });
4559 })
4560 }
4561
4562 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4563 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4564 s.move_heads_with(|map, head, _| {
4565 (movement::next_word_end(map, head), SelectionGoal::None)
4566 });
4567 })
4568 }
4569
4570 pub fn select_to_next_subword_end(
4571 &mut self,
4572 _: &SelectToNextSubwordEnd,
4573 cx: &mut ViewContext<Self>,
4574 ) {
4575 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4576 s.move_heads_with(|map, head, _| {
4577 (movement::next_subword_end(map, head), SelectionGoal::None)
4578 });
4579 })
4580 }
4581
4582 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4583 self.transact(cx, |this, cx| {
4584 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4585 let line_mode = s.line_mode;
4586 s.move_with(|map, selection| {
4587 if selection.is_empty() && !line_mode {
4588 let cursor = movement::next_word_end(map, selection.head());
4589 selection.set_head(cursor, SelectionGoal::None);
4590 }
4591 });
4592 });
4593 this.insert("", cx);
4594 });
4595 }
4596
4597 pub fn delete_to_next_subword_end(
4598 &mut self,
4599 _: &DeleteToNextSubwordEnd,
4600 cx: &mut ViewContext<Self>,
4601 ) {
4602 self.transact(cx, |this, cx| {
4603 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4604 s.move_with(|map, selection| {
4605 if selection.is_empty() {
4606 let cursor = movement::next_subword_end(map, selection.head());
4607 selection.set_head(cursor, SelectionGoal::None);
4608 }
4609 });
4610 });
4611 this.insert("", cx);
4612 });
4613 }
4614
4615 pub fn move_to_beginning_of_line(
4616 &mut self,
4617 _: &MoveToBeginningOfLine,
4618 cx: &mut ViewContext<Self>,
4619 ) {
4620 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4621 s.move_cursors_with(|map, head, _| {
4622 (
4623 movement::indented_line_beginning(map, head, true),
4624 SelectionGoal::None,
4625 )
4626 });
4627 })
4628 }
4629
4630 pub fn select_to_beginning_of_line(
4631 &mut self,
4632 action: &SelectToBeginningOfLine,
4633 cx: &mut ViewContext<Self>,
4634 ) {
4635 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4636 s.move_heads_with(|map, head, _| {
4637 (
4638 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4639 SelectionGoal::None,
4640 )
4641 });
4642 });
4643 }
4644
4645 pub fn delete_to_beginning_of_line(
4646 &mut self,
4647 _: &DeleteToBeginningOfLine,
4648 cx: &mut ViewContext<Self>,
4649 ) {
4650 self.transact(cx, |this, cx| {
4651 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4652 s.move_with(|_, selection| {
4653 selection.reversed = true;
4654 });
4655 });
4656
4657 this.select_to_beginning_of_line(
4658 &SelectToBeginningOfLine {
4659 stop_at_soft_wraps: false,
4660 },
4661 cx,
4662 );
4663 this.backspace(&Backspace, cx);
4664 });
4665 }
4666
4667 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4668 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4669 s.move_cursors_with(|map, head, _| {
4670 (movement::line_end(map, head, true), SelectionGoal::None)
4671 });
4672 })
4673 }
4674
4675 pub fn select_to_end_of_line(
4676 &mut self,
4677 action: &SelectToEndOfLine,
4678 cx: &mut ViewContext<Self>,
4679 ) {
4680 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4681 s.move_heads_with(|map, head, _| {
4682 (
4683 movement::line_end(map, head, action.stop_at_soft_wraps),
4684 SelectionGoal::None,
4685 )
4686 });
4687 })
4688 }
4689
4690 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4691 self.transact(cx, |this, cx| {
4692 this.select_to_end_of_line(
4693 &SelectToEndOfLine {
4694 stop_at_soft_wraps: false,
4695 },
4696 cx,
4697 );
4698 this.delete(&Delete, cx);
4699 });
4700 }
4701
4702 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4703 self.transact(cx, |this, cx| {
4704 this.select_to_end_of_line(
4705 &SelectToEndOfLine {
4706 stop_at_soft_wraps: false,
4707 },
4708 cx,
4709 );
4710 this.cut(&Cut, cx);
4711 });
4712 }
4713
4714 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4715 if matches!(self.mode, EditorMode::SingleLine) {
4716 cx.propagate_action();
4717 return;
4718 }
4719
4720 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4721 s.select_ranges(vec![0..0]);
4722 });
4723 }
4724
4725 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4726 let mut selection = self.selections.last::<Point>(cx);
4727 selection.set_head(Point::zero(), SelectionGoal::None);
4728
4729 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4730 s.select(vec![selection]);
4731 });
4732 }
4733
4734 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4735 if matches!(self.mode, EditorMode::SingleLine) {
4736 cx.propagate_action();
4737 return;
4738 }
4739
4740 let cursor = self.buffer.read(cx).read(cx).len();
4741 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4742 s.select_ranges(vec![cursor..cursor])
4743 });
4744 }
4745
4746 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4747 self.nav_history = nav_history;
4748 }
4749
4750 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4751 self.nav_history.as_ref()
4752 }
4753
4754 fn push_to_nav_history(
4755 &self,
4756 cursor_anchor: Anchor,
4757 new_position: Option<Point>,
4758 cx: &mut ViewContext<Self>,
4759 ) {
4760 if let Some(nav_history) = &self.nav_history {
4761 let buffer = self.buffer.read(cx).read(cx);
4762 let cursor_position = cursor_anchor.to_point(&buffer);
4763 let scroll_state = self.scroll_manager.anchor();
4764 let scroll_top_row = scroll_state.top_row(&buffer);
4765 drop(buffer);
4766
4767 if let Some(new_position) = new_position {
4768 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
4769 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4770 return;
4771 }
4772 }
4773
4774 nav_history.push(
4775 Some(NavigationData {
4776 cursor_anchor,
4777 cursor_position,
4778 scroll_anchor: scroll_state,
4779 scroll_top_row,
4780 }),
4781 cx,
4782 );
4783 }
4784 }
4785
4786 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4787 let buffer = self.buffer.read(cx).snapshot(cx);
4788 let mut selection = self.selections.first::<usize>(cx);
4789 selection.set_head(buffer.len(), SelectionGoal::None);
4790 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4791 s.select(vec![selection]);
4792 });
4793 }
4794
4795 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4796 let end = self.buffer.read(cx).read(cx).len();
4797 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4798 s.select_ranges(vec![0..end]);
4799 });
4800 }
4801
4802 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4803 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4804 let mut selections = self.selections.all::<Point>(cx);
4805 let max_point = display_map.buffer_snapshot.max_point();
4806 for selection in &mut selections {
4807 let rows = selection.spanned_rows(true, &display_map);
4808 selection.start = Point::new(rows.start, 0);
4809 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4810 selection.reversed = false;
4811 }
4812 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4813 s.select(selections);
4814 });
4815 }
4816
4817 pub fn split_selection_into_lines(
4818 &mut self,
4819 _: &SplitSelectionIntoLines,
4820 cx: &mut ViewContext<Self>,
4821 ) {
4822 let mut to_unfold = Vec::new();
4823 let mut new_selection_ranges = Vec::new();
4824 {
4825 let selections = self.selections.all::<Point>(cx);
4826 let buffer = self.buffer.read(cx).read(cx);
4827 for selection in selections {
4828 for row in selection.start.row..selection.end.row {
4829 let cursor = Point::new(row, buffer.line_len(row));
4830 new_selection_ranges.push(cursor..cursor);
4831 }
4832 new_selection_ranges.push(selection.end..selection.end);
4833 to_unfold.push(selection.start..selection.end);
4834 }
4835 }
4836 self.unfold_ranges(to_unfold, true, true, cx);
4837 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4838 s.select_ranges(new_selection_ranges);
4839 });
4840 }
4841
4842 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4843 self.add_selection(true, cx);
4844 }
4845
4846 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4847 self.add_selection(false, cx);
4848 }
4849
4850 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4851 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4852 let mut selections = self.selections.all::<Point>(cx);
4853 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4854 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4855 let range = oldest_selection.display_range(&display_map).sorted();
4856 let columns = cmp::min(range.start.column(), range.end.column())
4857 ..cmp::max(range.start.column(), range.end.column());
4858
4859 selections.clear();
4860 let mut stack = Vec::new();
4861 for row in range.start.row()..=range.end.row() {
4862 if let Some(selection) = self.selections.build_columnar_selection(
4863 &display_map,
4864 row,
4865 &columns,
4866 oldest_selection.reversed,
4867 ) {
4868 stack.push(selection.id);
4869 selections.push(selection);
4870 }
4871 }
4872
4873 if above {
4874 stack.reverse();
4875 }
4876
4877 AddSelectionsState { above, stack }
4878 });
4879
4880 let last_added_selection = *state.stack.last().unwrap();
4881 let mut new_selections = Vec::new();
4882 if above == state.above {
4883 let end_row = if above {
4884 0
4885 } else {
4886 display_map.max_point().row()
4887 };
4888
4889 'outer: for selection in selections {
4890 if selection.id == last_added_selection {
4891 let range = selection.display_range(&display_map).sorted();
4892 debug_assert_eq!(range.start.row(), range.end.row());
4893 let mut row = range.start.row();
4894 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4895 {
4896 start..end
4897 } else {
4898 cmp::min(range.start.column(), range.end.column())
4899 ..cmp::max(range.start.column(), range.end.column())
4900 };
4901
4902 while row != end_row {
4903 if above {
4904 row -= 1;
4905 } else {
4906 row += 1;
4907 }
4908
4909 if let Some(new_selection) = self.selections.build_columnar_selection(
4910 &display_map,
4911 row,
4912 &columns,
4913 selection.reversed,
4914 ) {
4915 state.stack.push(new_selection.id);
4916 if above {
4917 new_selections.push(new_selection);
4918 new_selections.push(selection);
4919 } else {
4920 new_selections.push(selection);
4921 new_selections.push(new_selection);
4922 }
4923
4924 continue 'outer;
4925 }
4926 }
4927 }
4928
4929 new_selections.push(selection);
4930 }
4931 } else {
4932 new_selections = selections;
4933 new_selections.retain(|s| s.id != last_added_selection);
4934 state.stack.pop();
4935 }
4936
4937 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4938 s.select(new_selections);
4939 });
4940 if state.stack.len() > 1 {
4941 self.add_selections_state = Some(state);
4942 }
4943 }
4944
4945 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4946 self.push_to_selection_history();
4947 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4948 let buffer = &display_map.buffer_snapshot;
4949 let mut selections = self.selections.all::<usize>(cx);
4950 if let Some(mut select_next_state) = self.select_next_state.take() {
4951 let query = &select_next_state.query;
4952 if !select_next_state.done {
4953 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4954 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4955 let mut next_selected_range = None;
4956
4957 let bytes_after_last_selection =
4958 buffer.bytes_in_range(last_selection.end..buffer.len());
4959 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4960 let query_matches = query
4961 .stream_find_iter(bytes_after_last_selection)
4962 .map(|result| (last_selection.end, result))
4963 .chain(
4964 query
4965 .stream_find_iter(bytes_before_first_selection)
4966 .map(|result| (0, result)),
4967 );
4968 for (start_offset, query_match) in query_matches {
4969 let query_match = query_match.unwrap(); // can only fail due to I/O
4970 let offset_range =
4971 start_offset + query_match.start()..start_offset + query_match.end();
4972 let display_range = offset_range.start.to_display_point(&display_map)
4973 ..offset_range.end.to_display_point(&display_map);
4974
4975 if !select_next_state.wordwise
4976 || (!movement::is_inside_word(&display_map, display_range.start)
4977 && !movement::is_inside_word(&display_map, display_range.end))
4978 {
4979 next_selected_range = Some(offset_range);
4980 break;
4981 }
4982 }
4983
4984 if let Some(next_selected_range) = next_selected_range {
4985 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
4986 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
4987 if action.replace_newest {
4988 s.delete(s.newest_anchor().id);
4989 }
4990 s.insert_range(next_selected_range);
4991 });
4992 } else {
4993 select_next_state.done = true;
4994 }
4995 }
4996
4997 self.select_next_state = Some(select_next_state);
4998 } else if selections.len() == 1 {
4999 let selection = selections.last_mut().unwrap();
5000 if selection.start == selection.end {
5001 let word_range = movement::surrounding_word(
5002 &display_map,
5003 selection.start.to_display_point(&display_map),
5004 );
5005 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5006 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5007 selection.goal = SelectionGoal::None;
5008 selection.reversed = false;
5009
5010 let query = buffer
5011 .text_for_range(selection.start..selection.end)
5012 .collect::<String>();
5013 let select_state = SelectNextState {
5014 query: AhoCorasick::new_auto_configured(&[query]),
5015 wordwise: true,
5016 done: false,
5017 };
5018 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5019 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5020 s.select(selections);
5021 });
5022 self.select_next_state = Some(select_state);
5023 } else {
5024 let query = buffer
5025 .text_for_range(selection.start..selection.end)
5026 .collect::<String>();
5027 self.select_next_state = Some(SelectNextState {
5028 query: AhoCorasick::new_auto_configured(&[query]),
5029 wordwise: false,
5030 done: false,
5031 });
5032 self.select_next(action, cx);
5033 }
5034 }
5035 }
5036
5037 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5038 self.transact(cx, |this, cx| {
5039 let mut selections = this.selections.all::<Point>(cx);
5040 let mut edits = Vec::new();
5041 let mut selection_edit_ranges = Vec::new();
5042 let mut last_toggled_row = None;
5043 let snapshot = this.buffer.read(cx).read(cx);
5044 let empty_str: Arc<str> = "".into();
5045 let mut suffixes_inserted = Vec::new();
5046
5047 fn comment_prefix_range(
5048 snapshot: &MultiBufferSnapshot,
5049 row: u32,
5050 comment_prefix: &str,
5051 comment_prefix_whitespace: &str,
5052 ) -> Range<Point> {
5053 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5054
5055 let mut line_bytes = snapshot
5056 .bytes_in_range(start..snapshot.max_point())
5057 .flatten()
5058 .copied();
5059
5060 // If this line currently begins with the line comment prefix, then record
5061 // the range containing the prefix.
5062 if line_bytes
5063 .by_ref()
5064 .take(comment_prefix.len())
5065 .eq(comment_prefix.bytes())
5066 {
5067 // Include any whitespace that matches the comment prefix.
5068 let matching_whitespace_len = line_bytes
5069 .zip(comment_prefix_whitespace.bytes())
5070 .take_while(|(a, b)| a == b)
5071 .count() as u32;
5072 let end = Point::new(
5073 start.row,
5074 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5075 );
5076 start..end
5077 } else {
5078 start..start
5079 }
5080 }
5081
5082 fn comment_suffix_range(
5083 snapshot: &MultiBufferSnapshot,
5084 row: u32,
5085 comment_suffix: &str,
5086 comment_suffix_has_leading_space: bool,
5087 ) -> Range<Point> {
5088 let end = Point::new(row, snapshot.line_len(row));
5089 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5090
5091 let mut line_end_bytes = snapshot
5092 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5093 .flatten()
5094 .copied();
5095
5096 let leading_space_len = if suffix_start_column > 0
5097 && line_end_bytes.next() == Some(b' ')
5098 && comment_suffix_has_leading_space
5099 {
5100 1
5101 } else {
5102 0
5103 };
5104
5105 // If this line currently begins with the line comment prefix, then record
5106 // the range containing the prefix.
5107 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5108 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5109 start..end
5110 } else {
5111 end..end
5112 }
5113 }
5114
5115 // TODO: Handle selections that cross excerpts
5116 for selection in &mut selections {
5117 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5118 let language = if let Some(language) =
5119 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5120 {
5121 language
5122 } else {
5123 continue;
5124 };
5125
5126 selection_edit_ranges.clear();
5127
5128 // If multiple selections contain a given row, avoid processing that
5129 // row more than once.
5130 let mut start_row = selection.start.row;
5131 if last_toggled_row == Some(start_row) {
5132 start_row += 1;
5133 }
5134 let end_row =
5135 if selection.end.row > selection.start.row && selection.end.column == 0 {
5136 selection.end.row - 1
5137 } else {
5138 selection.end.row
5139 };
5140 last_toggled_row = Some(end_row);
5141
5142 if start_row > end_row {
5143 continue;
5144 }
5145
5146 // If the language has line comments, toggle those.
5147 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5148 // Split the comment prefix's trailing whitespace into a separate string,
5149 // as that portion won't be used for detecting if a line is a comment.
5150 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5151 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5152 let mut all_selection_lines_are_comments = true;
5153
5154 for row in start_row..=end_row {
5155 if snapshot.is_line_blank(row) {
5156 continue;
5157 }
5158
5159 let prefix_range = comment_prefix_range(
5160 snapshot.deref(),
5161 row,
5162 comment_prefix,
5163 comment_prefix_whitespace,
5164 );
5165 if prefix_range.is_empty() {
5166 all_selection_lines_are_comments = false;
5167 }
5168 selection_edit_ranges.push(prefix_range);
5169 }
5170
5171 if all_selection_lines_are_comments {
5172 edits.extend(
5173 selection_edit_ranges
5174 .iter()
5175 .cloned()
5176 .map(|range| (range, empty_str.clone())),
5177 );
5178 } else {
5179 let min_column = selection_edit_ranges
5180 .iter()
5181 .map(|r| r.start.column)
5182 .min()
5183 .unwrap_or(0);
5184 edits.extend(selection_edit_ranges.iter().map(|range| {
5185 let position = Point::new(range.start.row, min_column);
5186 (position..position, full_comment_prefix.clone())
5187 }));
5188 }
5189 } else if let Some((full_comment_prefix, comment_suffix)) =
5190 language.block_comment_delimiters()
5191 {
5192 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5193 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5194 let prefix_range = comment_prefix_range(
5195 snapshot.deref(),
5196 start_row,
5197 comment_prefix,
5198 comment_prefix_whitespace,
5199 );
5200 let suffix_range = comment_suffix_range(
5201 snapshot.deref(),
5202 end_row,
5203 comment_suffix.trim_start_matches(' '),
5204 comment_suffix.starts_with(' '),
5205 );
5206
5207 if prefix_range.is_empty() || suffix_range.is_empty() {
5208 edits.push((
5209 prefix_range.start..prefix_range.start,
5210 full_comment_prefix.clone(),
5211 ));
5212 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5213 suffixes_inserted.push((end_row, comment_suffix.len()));
5214 } else {
5215 edits.push((prefix_range, empty_str.clone()));
5216 edits.push((suffix_range, empty_str.clone()));
5217 }
5218 } else {
5219 continue;
5220 }
5221 }
5222
5223 drop(snapshot);
5224 this.buffer.update(cx, |buffer, cx| {
5225 buffer.edit(edits, None, cx);
5226 });
5227
5228 // Adjust selections so that they end before any comment suffixes that
5229 // were inserted.
5230 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5231 let mut selections = this.selections.all::<Point>(cx);
5232 let snapshot = this.buffer.read(cx).read(cx);
5233 for selection in &mut selections {
5234 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5235 match row.cmp(&selection.end.row) {
5236 Ordering::Less => {
5237 suffixes_inserted.next();
5238 continue;
5239 }
5240 Ordering::Greater => break,
5241 Ordering::Equal => {
5242 if selection.end.column == snapshot.line_len(row) {
5243 if selection.is_empty() {
5244 selection.start.column -= suffix_len as u32;
5245 }
5246 selection.end.column -= suffix_len as u32;
5247 }
5248 break;
5249 }
5250 }
5251 }
5252 }
5253
5254 drop(snapshot);
5255 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5256
5257 let selections = this.selections.all::<Point>(cx);
5258 let selections_on_single_row = selections.windows(2).all(|selections| {
5259 selections[0].start.row == selections[1].start.row
5260 && selections[0].end.row == selections[1].end.row
5261 && selections[0].start.row == selections[0].end.row
5262 });
5263 let selections_selecting = selections
5264 .iter()
5265 .any(|selection| selection.start != selection.end);
5266 let advance_downwards = action.advance_downwards
5267 && selections_on_single_row
5268 && !selections_selecting
5269 && this.mode != EditorMode::SingleLine;
5270
5271 if advance_downwards {
5272 let snapshot = this.buffer.read(cx).snapshot(cx);
5273
5274 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5275 s.move_cursors_with(|display_snapshot, display_point, _| {
5276 let mut point = display_point.to_point(display_snapshot);
5277 point.row += 1;
5278 point = snapshot.clip_point(point, Bias::Left);
5279 let display_point = point.to_display_point(display_snapshot);
5280 (display_point, SelectionGoal::Column(display_point.column()))
5281 })
5282 });
5283 }
5284 });
5285 }
5286
5287 pub fn select_larger_syntax_node(
5288 &mut self,
5289 _: &SelectLargerSyntaxNode,
5290 cx: &mut ViewContext<Self>,
5291 ) {
5292 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5293 let buffer = self.buffer.read(cx).snapshot(cx);
5294 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5295
5296 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5297 let mut selected_larger_node = false;
5298 let new_selections = old_selections
5299 .iter()
5300 .map(|selection| {
5301 let old_range = selection.start..selection.end;
5302 let mut new_range = old_range.clone();
5303 while let Some(containing_range) =
5304 buffer.range_for_syntax_ancestor(new_range.clone())
5305 {
5306 new_range = containing_range;
5307 if !display_map.intersects_fold(new_range.start)
5308 && !display_map.intersects_fold(new_range.end)
5309 {
5310 break;
5311 }
5312 }
5313
5314 selected_larger_node |= new_range != old_range;
5315 Selection {
5316 id: selection.id,
5317 start: new_range.start,
5318 end: new_range.end,
5319 goal: SelectionGoal::None,
5320 reversed: selection.reversed,
5321 }
5322 })
5323 .collect::<Vec<_>>();
5324
5325 if selected_larger_node {
5326 stack.push(old_selections);
5327 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5328 s.select(new_selections);
5329 });
5330 }
5331 self.select_larger_syntax_node_stack = stack;
5332 }
5333
5334 pub fn select_smaller_syntax_node(
5335 &mut self,
5336 _: &SelectSmallerSyntaxNode,
5337 cx: &mut ViewContext<Self>,
5338 ) {
5339 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5340 if let Some(selections) = stack.pop() {
5341 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5342 s.select(selections.to_vec());
5343 });
5344 }
5345 self.select_larger_syntax_node_stack = stack;
5346 }
5347
5348 pub fn move_to_enclosing_bracket(
5349 &mut self,
5350 _: &MoveToEnclosingBracket,
5351 cx: &mut ViewContext<Self>,
5352 ) {
5353 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5354 s.move_offsets_with(|snapshot, selection| {
5355 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5356 return;
5357 };
5358
5359 let mut best_length = usize::MAX;
5360 let mut best_inside = false;
5361 let mut best_in_bracket_range = false;
5362 let mut best_destination = None;
5363 for (open, close) in enclosing_bracket_ranges {
5364 let close = close.to_inclusive();
5365 let length = close.end() - open.start;
5366 let inside = selection.start >= open.end && selection.end <= *close.start();
5367 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5368
5369 // If best is next to a bracket and current isn't, skip
5370 if !in_bracket_range && best_in_bracket_range {
5371 continue;
5372 }
5373
5374 // Prefer smaller lengths unless best is inside and current isn't
5375 if length > best_length && (best_inside || !inside) {
5376 continue;
5377 }
5378
5379 best_length = length;
5380 best_inside = inside;
5381 best_in_bracket_range = in_bracket_range;
5382 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5383 if inside {
5384 open.end
5385 } else {
5386 open.start
5387 }
5388 } else {
5389 if inside {
5390 *close.start()
5391 } else {
5392 *close.end()
5393 }
5394 });
5395 }
5396
5397 if let Some(destination) = best_destination {
5398 selection.collapse_to(destination, SelectionGoal::None);
5399 }
5400 })
5401 });
5402 }
5403
5404 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5405 self.end_selection(cx);
5406 self.selection_history.mode = SelectionHistoryMode::Undoing;
5407 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5408 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5409 self.select_next_state = entry.select_next_state;
5410 self.add_selections_state = entry.add_selections_state;
5411 self.request_autoscroll(Autoscroll::newest(), cx);
5412 }
5413 self.selection_history.mode = SelectionHistoryMode::Normal;
5414 }
5415
5416 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5417 self.end_selection(cx);
5418 self.selection_history.mode = SelectionHistoryMode::Redoing;
5419 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5420 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5421 self.select_next_state = entry.select_next_state;
5422 self.add_selections_state = entry.add_selections_state;
5423 self.request_autoscroll(Autoscroll::newest(), cx);
5424 }
5425 self.selection_history.mode = SelectionHistoryMode::Normal;
5426 }
5427
5428 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5429 self.go_to_diagnostic_impl(Direction::Next, cx)
5430 }
5431
5432 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5433 self.go_to_diagnostic_impl(Direction::Prev, cx)
5434 }
5435
5436 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5437 let buffer = self.buffer.read(cx).snapshot(cx);
5438 let selection = self.selections.newest::<usize>(cx);
5439
5440 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5441 if direction == Direction::Next {
5442 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5443 let (group_id, jump_to) = popover.activation_info();
5444 if self.activate_diagnostics(group_id, cx) {
5445 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5446 let mut new_selection = s.newest_anchor().clone();
5447 new_selection.collapse_to(jump_to, SelectionGoal::None);
5448 s.select_anchors(vec![new_selection.clone()]);
5449 });
5450 }
5451 return;
5452 }
5453 }
5454
5455 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5456 active_diagnostics
5457 .primary_range
5458 .to_offset(&buffer)
5459 .to_inclusive()
5460 });
5461 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5462 if active_primary_range.contains(&selection.head()) {
5463 *active_primary_range.end()
5464 } else {
5465 selection.head()
5466 }
5467 } else {
5468 selection.head()
5469 };
5470
5471 loop {
5472 let mut diagnostics = if direction == Direction::Prev {
5473 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5474 } else {
5475 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5476 };
5477 let group = diagnostics.find_map(|entry| {
5478 if entry.diagnostic.is_primary
5479 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5480 && !entry.range.is_empty()
5481 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5482 {
5483 Some((entry.range, entry.diagnostic.group_id))
5484 } else {
5485 None
5486 }
5487 });
5488
5489 if let Some((primary_range, group_id)) = group {
5490 if self.activate_diagnostics(group_id, cx) {
5491 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5492 s.select(vec![Selection {
5493 id: selection.id,
5494 start: primary_range.start,
5495 end: primary_range.start,
5496 reversed: false,
5497 goal: SelectionGoal::None,
5498 }]);
5499 });
5500 }
5501 break;
5502 } else {
5503 // Cycle around to the start of the buffer, potentially moving back to the start of
5504 // the currently active diagnostic.
5505 active_primary_range.take();
5506 if direction == Direction::Prev {
5507 if search_start == buffer.len() {
5508 break;
5509 } else {
5510 search_start = buffer.len();
5511 }
5512 } else if search_start == 0 {
5513 break;
5514 } else {
5515 search_start = 0;
5516 }
5517 }
5518 }
5519 }
5520
5521 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5522 self.go_to_hunk_impl(Direction::Next, cx)
5523 }
5524
5525 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5526 self.go_to_hunk_impl(Direction::Prev, cx)
5527 }
5528
5529 pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5530 let snapshot = self
5531 .display_map
5532 .update(cx, |display_map, cx| display_map.snapshot(cx));
5533 let selection = self.selections.newest::<Point>(cx);
5534
5535 fn seek_in_direction(
5536 this: &mut Editor,
5537 snapshot: &DisplaySnapshot,
5538 initial_point: Point,
5539 is_wrapped: bool,
5540 direction: Direction,
5541 cx: &mut ViewContext<Editor>,
5542 ) -> bool {
5543 let hunks = if direction == Direction::Next {
5544 snapshot
5545 .buffer_snapshot
5546 .git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
5547 } else {
5548 snapshot
5549 .buffer_snapshot
5550 .git_diff_hunks_in_range(0..initial_point.row, true)
5551 };
5552
5553 let display_point = initial_point.to_display_point(snapshot);
5554 let mut hunks = hunks
5555 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5556 .skip_while(|hunk| {
5557 if is_wrapped {
5558 false
5559 } else {
5560 hunk.contains_display_row(display_point.row())
5561 }
5562 })
5563 .dedup();
5564
5565 if let Some(hunk) = hunks.next() {
5566 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5567 let row = hunk.start_display_row();
5568 let point = DisplayPoint::new(row, 0);
5569 s.select_display_ranges([point..point]);
5570 });
5571
5572 true
5573 } else {
5574 false
5575 }
5576 }
5577
5578 if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
5579 let wrapped_point = match direction {
5580 Direction::Next => Point::zero(),
5581 Direction::Prev => snapshot.buffer_snapshot.max_point(),
5582 };
5583 seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
5584 }
5585 }
5586
5587 pub fn go_to_definition(
5588 workspace: &mut Workspace,
5589 _: &GoToDefinition,
5590 cx: &mut ViewContext<Workspace>,
5591 ) {
5592 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
5593 }
5594
5595 pub fn go_to_type_definition(
5596 workspace: &mut Workspace,
5597 _: &GoToTypeDefinition,
5598 cx: &mut ViewContext<Workspace>,
5599 ) {
5600 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
5601 }
5602
5603 fn go_to_definition_of_kind(
5604 kind: GotoDefinitionKind,
5605 workspace: &mut Workspace,
5606 cx: &mut ViewContext<Workspace>,
5607 ) {
5608 let active_item = workspace.active_item(cx);
5609 let editor_handle = if let Some(editor) = active_item
5610 .as_ref()
5611 .and_then(|item| item.act_as::<Self>(cx))
5612 {
5613 editor
5614 } else {
5615 return;
5616 };
5617
5618 let editor = editor_handle.read(cx);
5619 let buffer = editor.buffer.read(cx);
5620 let head = editor.selections.newest::<usize>(cx).head();
5621 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5622 text_anchor
5623 } else {
5624 return;
5625 };
5626
5627 let project = workspace.project().clone();
5628 let definitions = project.update(cx, |project, cx| match kind {
5629 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5630 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5631 });
5632
5633 cx.spawn_labeled("Fetching Definition...", |workspace, mut cx| async move {
5634 let definitions = definitions.await?;
5635 workspace.update(&mut cx, |workspace, cx| {
5636 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
5637 })?;
5638
5639 Ok::<(), anyhow::Error>(())
5640 })
5641 .detach_and_log_err(cx);
5642 }
5643
5644 pub fn navigate_to_definitions(
5645 workspace: &mut Workspace,
5646 editor_handle: ViewHandle<Editor>,
5647 definitions: Vec<LocationLink>,
5648 cx: &mut ViewContext<Workspace>,
5649 ) {
5650 let pane = workspace.active_pane().clone();
5651 // If there is one definition, just open it directly
5652 if let [definition] = definitions.as_slice() {
5653 let range = definition
5654 .target
5655 .range
5656 .to_offset(definition.target.buffer.read(cx));
5657
5658 let target_editor_handle =
5659 workspace.open_project_item(definition.target.buffer.clone(), cx);
5660 target_editor_handle.update(cx, |target_editor, cx| {
5661 // When selecting a definition in a different buffer, disable the nav history
5662 // to avoid creating a history entry at the previous cursor location.
5663 if editor_handle != target_editor_handle {
5664 pane.update(cx, |pane, _| pane.disable_history());
5665 }
5666 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5667 s.select_ranges([range]);
5668 });
5669
5670 pane.update(cx, |pane, _| pane.enable_history());
5671 });
5672 } else if !definitions.is_empty() {
5673 let replica_id = editor_handle.read(cx).replica_id(cx);
5674 let title = definitions
5675 .iter()
5676 .find(|definition| definition.origin.is_some())
5677 .and_then(|definition| {
5678 definition.origin.as_ref().map(|origin| {
5679 let buffer = origin.buffer.read(cx);
5680 format!(
5681 "Definitions for {}",
5682 buffer
5683 .text_for_range(origin.range.clone())
5684 .collect::<String>()
5685 )
5686 })
5687 })
5688 .unwrap_or("Definitions".to_owned());
5689 let locations = definitions
5690 .into_iter()
5691 .map(|definition| definition.target)
5692 .collect();
5693 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5694 }
5695 }
5696
5697 pub fn find_all_references(
5698 workspace: &mut Workspace,
5699 _: &FindAllReferences,
5700 cx: &mut ViewContext<Workspace>,
5701 ) -> Option<Task<Result<()>>> {
5702 let active_item = workspace.active_item(cx)?;
5703 let editor_handle = active_item.act_as::<Self>(cx)?;
5704
5705 let editor = editor_handle.read(cx);
5706 let buffer = editor.buffer.read(cx);
5707 let head = editor.selections.newest::<usize>(cx).head();
5708 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5709 let replica_id = editor.replica_id(cx);
5710
5711 let project = workspace.project().clone();
5712 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5713 Some(cx.spawn_labeled(
5714 "Finding All References...",
5715 |workspace, mut cx| async move {
5716 let locations = references.await?;
5717 if locations.is_empty() {
5718 return Ok(());
5719 }
5720
5721 workspace.update(&mut cx, |workspace, cx| {
5722 let title = locations
5723 .first()
5724 .as_ref()
5725 .map(|location| {
5726 let buffer = location.buffer.read(cx);
5727 format!(
5728 "References to `{}`",
5729 buffer
5730 .text_for_range(location.range.clone())
5731 .collect::<String>()
5732 )
5733 })
5734 .unwrap();
5735 Self::open_locations_in_multibuffer(
5736 workspace, locations, replica_id, title, cx,
5737 );
5738 })?;
5739
5740 Ok(())
5741 },
5742 ))
5743 }
5744
5745 /// Opens a multibuffer with the given project locations in it
5746 pub fn open_locations_in_multibuffer(
5747 workspace: &mut Workspace,
5748 mut locations: Vec<Location>,
5749 replica_id: ReplicaId,
5750 title: String,
5751 cx: &mut ViewContext<Workspace>,
5752 ) {
5753 // If there are multiple definitions, open them in a multibuffer
5754 locations.sort_by_key(|location| location.buffer.id());
5755 let mut locations = locations.into_iter().peekable();
5756 let mut ranges_to_highlight = Vec::new();
5757
5758 let excerpt_buffer = cx.add_model(|cx| {
5759 let mut multibuffer = MultiBuffer::new(replica_id);
5760 while let Some(location) = locations.next() {
5761 let buffer = location.buffer.read(cx);
5762 let mut ranges_for_buffer = Vec::new();
5763 let range = location.range.to_offset(buffer);
5764 ranges_for_buffer.push(range.clone());
5765
5766 while let Some(next_location) = locations.peek() {
5767 if next_location.buffer == location.buffer {
5768 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5769 locations.next();
5770 } else {
5771 break;
5772 }
5773 }
5774
5775 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5776 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5777 location.buffer.clone(),
5778 ranges_for_buffer,
5779 1,
5780 cx,
5781 ))
5782 }
5783
5784 multibuffer.with_title(title)
5785 });
5786
5787 let editor = cx.add_view(|cx| {
5788 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5789 });
5790 editor.update(cx, |editor, cx| {
5791 editor.highlight_background::<Self>(
5792 ranges_to_highlight,
5793 |theme| theme.editor.highlighted_line_background,
5794 cx,
5795 );
5796 });
5797 workspace.add_item(Box::new(editor), cx);
5798 }
5799
5800 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5801 use language::ToOffset as _;
5802
5803 let project = self.project.clone()?;
5804 let selection = self.selections.newest_anchor().clone();
5805 let (cursor_buffer, cursor_buffer_position) = self
5806 .buffer
5807 .read(cx)
5808 .text_anchor_for_position(selection.head(), cx)?;
5809 let (tail_buffer, _) = self
5810 .buffer
5811 .read(cx)
5812 .text_anchor_for_position(selection.tail(), cx)?;
5813 if tail_buffer != cursor_buffer {
5814 return None;
5815 }
5816
5817 let snapshot = cursor_buffer.read(cx).snapshot();
5818 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5819 let prepare_rename = project.update(cx, |project, cx| {
5820 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5821 });
5822
5823 Some(cx.spawn(|this, mut cx| async move {
5824 let rename_range = if let Some(range) = prepare_rename.await? {
5825 Some(range)
5826 } else {
5827 this.read_with(&cx, |this, cx| {
5828 let buffer = this.buffer.read(cx).snapshot(cx);
5829 let mut buffer_highlights = this
5830 .document_highlights_for_position(selection.head(), &buffer)
5831 .filter(|highlight| {
5832 highlight.start.excerpt_id() == selection.head().excerpt_id()
5833 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5834 });
5835 buffer_highlights
5836 .next()
5837 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5838 })?
5839 };
5840 if let Some(rename_range) = rename_range {
5841 let rename_buffer_range = rename_range.to_offset(&snapshot);
5842 let cursor_offset_in_rename_range =
5843 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5844
5845 this.update(&mut cx, |this, cx| {
5846 this.take_rename(false, cx);
5847 let style = this.style(cx);
5848 let buffer = this.buffer.read(cx).read(cx);
5849 let cursor_offset = selection.head().to_offset(&buffer);
5850 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5851 let rename_end = rename_start + rename_buffer_range.len();
5852 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5853 let mut old_highlight_id = None;
5854 let old_name: Arc<str> = buffer
5855 .chunks(rename_start..rename_end, true)
5856 .map(|chunk| {
5857 if old_highlight_id.is_none() {
5858 old_highlight_id = chunk.syntax_highlight_id;
5859 }
5860 chunk.text
5861 })
5862 .collect::<String>()
5863 .into();
5864
5865 drop(buffer);
5866
5867 // Position the selection in the rename editor so that it matches the current selection.
5868 this.show_local_selections = false;
5869 let rename_editor = cx.add_view(|cx| {
5870 let mut editor = Editor::single_line(None, cx);
5871 if let Some(old_highlight_id) = old_highlight_id {
5872 editor.override_text_style =
5873 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5874 }
5875 editor.buffer.update(cx, |buffer, cx| {
5876 buffer.edit([(0..0, old_name.clone())], None, cx)
5877 });
5878 editor.select_all(&SelectAll, cx);
5879 editor
5880 });
5881
5882 let ranges = this
5883 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5884 .into_iter()
5885 .flat_map(|(_, ranges)| ranges)
5886 .chain(
5887 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5888 .into_iter()
5889 .flat_map(|(_, ranges)| ranges),
5890 )
5891 .collect();
5892
5893 this.highlight_text::<Rename>(
5894 ranges,
5895 HighlightStyle {
5896 fade_out: Some(style.rename_fade),
5897 ..Default::default()
5898 },
5899 cx,
5900 );
5901 cx.focus(&rename_editor);
5902 let block_id = this.insert_blocks(
5903 [BlockProperties {
5904 style: BlockStyle::Flex,
5905 position: range.start.clone(),
5906 height: 1,
5907 render: Arc::new({
5908 let editor = rename_editor.clone();
5909 move |cx: &mut BlockContext| {
5910 ChildView::new(&editor, cx)
5911 .contained()
5912 .with_padding_left(cx.anchor_x)
5913 .into_any()
5914 }
5915 }),
5916 disposition: BlockDisposition::Below,
5917 }],
5918 cx,
5919 )[0];
5920 this.pending_rename = Some(RenameState {
5921 range,
5922 old_name,
5923 editor: rename_editor,
5924 block_id,
5925 });
5926 })?;
5927 }
5928
5929 Ok(())
5930 }))
5931 }
5932
5933 pub fn confirm_rename(
5934 workspace: &mut Workspace,
5935 _: &ConfirmRename,
5936 cx: &mut ViewContext<Workspace>,
5937 ) -> Option<Task<Result<()>>> {
5938 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5939
5940 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5941 let rename = editor.take_rename(false, cx)?;
5942 let buffer = editor.buffer.read(cx);
5943 let (start_buffer, start) =
5944 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5945 let (end_buffer, end) =
5946 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5947 if start_buffer == end_buffer {
5948 let new_name = rename.editor.read(cx).text(cx);
5949 Some((start_buffer, start..end, rename.old_name, new_name))
5950 } else {
5951 None
5952 }
5953 })?;
5954
5955 let rename = workspace.project().clone().update(cx, |project, cx| {
5956 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5957 });
5958
5959 let editor = editor.downgrade();
5960 Some(cx.spawn(|workspace, mut cx| async move {
5961 let project_transaction = rename.await?;
5962 Self::open_project_transaction(
5963 &editor,
5964 workspace,
5965 project_transaction,
5966 format!("Rename: {} → {}", old_name, new_name),
5967 cx.clone(),
5968 )
5969 .await?;
5970
5971 editor.update(&mut cx, |editor, cx| {
5972 editor.refresh_document_highlights(cx);
5973 })?;
5974 Ok(())
5975 }))
5976 }
5977
5978 fn take_rename(
5979 &mut self,
5980 moving_cursor: bool,
5981 cx: &mut ViewContext<Self>,
5982 ) -> Option<RenameState> {
5983 let rename = self.pending_rename.take()?;
5984 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5985 self.clear_text_highlights::<Rename>(cx);
5986 self.show_local_selections = true;
5987
5988 if moving_cursor {
5989 let rename_editor = rename.editor.read(cx);
5990 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5991
5992 // Update the selection to match the position of the selection inside
5993 // the rename editor.
5994 let snapshot = self.buffer.read(cx).read(cx);
5995 let rename_range = rename.range.to_offset(&snapshot);
5996 let cursor_in_editor = snapshot
5997 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5998 .min(rename_range.end);
5999 drop(snapshot);
6000
6001 self.change_selections(None, cx, |s| {
6002 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6003 });
6004 } else {
6005 self.refresh_document_highlights(cx);
6006 }
6007
6008 Some(rename)
6009 }
6010
6011 #[cfg(any(test, feature = "test-support"))]
6012 pub fn pending_rename(&self) -> Option<&RenameState> {
6013 self.pending_rename.as_ref()
6014 }
6015
6016 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6017 let project = match &self.project {
6018 Some(project) => project.clone(),
6019 None => return None,
6020 };
6021
6022 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6023 }
6024
6025 fn perform_format(
6026 &mut self,
6027 project: ModelHandle<Project>,
6028 trigger: FormatTrigger,
6029 cx: &mut ViewContext<Self>,
6030 ) -> Task<Result<()>> {
6031 let buffer = self.buffer().clone();
6032 let buffers = buffer.read(cx).all_buffers();
6033
6034 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6035 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6036
6037 cx.spawn(|_, mut cx| async move {
6038 let transaction = futures::select_biased! {
6039 _ = timeout => {
6040 log::warn!("timed out waiting for formatting");
6041 None
6042 }
6043 transaction = format.log_err().fuse() => transaction,
6044 };
6045
6046 buffer.update(&mut cx, |buffer, cx| {
6047 if let Some(transaction) = transaction {
6048 if !buffer.is_singleton() {
6049 buffer.push_transaction(&transaction.0);
6050 }
6051 }
6052
6053 cx.notify();
6054 });
6055
6056 Ok(())
6057 })
6058 }
6059
6060 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6061 if let Some(project) = self.project.clone() {
6062 self.buffer.update(cx, |multi_buffer, cx| {
6063 project.update(cx, |project, cx| {
6064 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6065 });
6066 })
6067 }
6068 }
6069
6070 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6071 cx.show_character_palette();
6072 }
6073
6074 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6075 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6076 let buffer = self.buffer.read(cx).snapshot(cx);
6077 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6078 let is_valid = buffer
6079 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6080 .any(|entry| {
6081 entry.diagnostic.is_primary
6082 && !entry.range.is_empty()
6083 && entry.range.start == primary_range_start
6084 && entry.diagnostic.message == active_diagnostics.primary_message
6085 });
6086
6087 if is_valid != active_diagnostics.is_valid {
6088 active_diagnostics.is_valid = is_valid;
6089 let mut new_styles = HashMap::default();
6090 for (block_id, diagnostic) in &active_diagnostics.blocks {
6091 new_styles.insert(
6092 *block_id,
6093 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6094 );
6095 }
6096 self.display_map
6097 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6098 }
6099 }
6100 }
6101
6102 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6103 self.dismiss_diagnostics(cx);
6104 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6105 let buffer = self.buffer.read(cx).snapshot(cx);
6106
6107 let mut primary_range = None;
6108 let mut primary_message = None;
6109 let mut group_end = Point::zero();
6110 let diagnostic_group = buffer
6111 .diagnostic_group::<Point>(group_id)
6112 .map(|entry| {
6113 if entry.range.end > group_end {
6114 group_end = entry.range.end;
6115 }
6116 if entry.diagnostic.is_primary {
6117 primary_range = Some(entry.range.clone());
6118 primary_message = Some(entry.diagnostic.message.clone());
6119 }
6120 entry
6121 })
6122 .collect::<Vec<_>>();
6123 let primary_range = primary_range?;
6124 let primary_message = primary_message?;
6125 let primary_range =
6126 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6127
6128 let blocks = display_map
6129 .insert_blocks(
6130 diagnostic_group.iter().map(|entry| {
6131 let diagnostic = entry.diagnostic.clone();
6132 let message_height = diagnostic.message.lines().count() as u8;
6133 BlockProperties {
6134 style: BlockStyle::Fixed,
6135 position: buffer.anchor_after(entry.range.start),
6136 height: message_height,
6137 render: diagnostic_block_renderer(diagnostic, true),
6138 disposition: BlockDisposition::Below,
6139 }
6140 }),
6141 cx,
6142 )
6143 .into_iter()
6144 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6145 .collect();
6146
6147 Some(ActiveDiagnosticGroup {
6148 primary_range,
6149 primary_message,
6150 blocks,
6151 is_valid: true,
6152 })
6153 });
6154 self.active_diagnostics.is_some()
6155 }
6156
6157 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6158 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6159 self.display_map.update(cx, |display_map, cx| {
6160 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6161 });
6162 cx.notify();
6163 }
6164 }
6165
6166 pub fn set_selections_from_remote(
6167 &mut self,
6168 selections: Vec<Selection<Anchor>>,
6169 pending_selection: Option<Selection<Anchor>>,
6170 cx: &mut ViewContext<Self>,
6171 ) {
6172 let old_cursor_position = self.selections.newest_anchor().head();
6173 self.selections.change_with(cx, |s| {
6174 s.select_anchors(selections);
6175 if let Some(pending_selection) = pending_selection {
6176 s.set_pending(pending_selection, SelectMode::Character);
6177 } else {
6178 s.clear_pending();
6179 }
6180 });
6181 self.selections_did_change(false, &old_cursor_position, cx);
6182 }
6183
6184 fn push_to_selection_history(&mut self) {
6185 self.selection_history.push(SelectionHistoryEntry {
6186 selections: self.selections.disjoint_anchors(),
6187 select_next_state: self.select_next_state.clone(),
6188 add_selections_state: self.add_selections_state.clone(),
6189 });
6190 }
6191
6192 pub fn transact(
6193 &mut self,
6194 cx: &mut ViewContext<Self>,
6195 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6196 ) -> Option<TransactionId> {
6197 self.start_transaction_at(Instant::now(), cx);
6198 update(self, cx);
6199 self.end_transaction_at(Instant::now(), cx)
6200 }
6201
6202 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6203 self.end_selection(cx);
6204 if let Some(tx_id) = self
6205 .buffer
6206 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6207 {
6208 self.selection_history
6209 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6210 }
6211 }
6212
6213 fn end_transaction_at(
6214 &mut self,
6215 now: Instant,
6216 cx: &mut ViewContext<Self>,
6217 ) -> Option<TransactionId> {
6218 if let Some(tx_id) = self
6219 .buffer
6220 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6221 {
6222 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6223 *end_selections = Some(self.selections.disjoint_anchors());
6224 } else {
6225 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6226 }
6227
6228 cx.emit(Event::Edited);
6229 Some(tx_id)
6230 } else {
6231 None
6232 }
6233 }
6234
6235 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6236 let mut fold_ranges = Vec::new();
6237
6238 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6239
6240 let selections = self.selections.all::<Point>(cx);
6241 for selection in selections {
6242 let range = selection.range().sorted();
6243 let buffer_start_row = range.start.row;
6244
6245 for row in (0..=range.end.row).rev() {
6246 let fold_range = display_map.foldable_range(row);
6247
6248 if let Some(fold_range) = fold_range {
6249 if fold_range.end.row >= buffer_start_row {
6250 fold_ranges.push(fold_range);
6251 if row <= range.start.row {
6252 break;
6253 }
6254 }
6255 }
6256 }
6257 }
6258
6259 self.fold_ranges(fold_ranges, true, cx);
6260 }
6261
6262 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6263 let buffer_row = fold_at.buffer_row;
6264 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6265
6266 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6267 let autoscroll = self
6268 .selections
6269 .all::<Point>(cx)
6270 .iter()
6271 .any(|selection| fold_range.overlaps(&selection.range()));
6272
6273 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6274 }
6275 }
6276
6277 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6278 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6279 let buffer = &display_map.buffer_snapshot;
6280 let selections = self.selections.all::<Point>(cx);
6281 let ranges = selections
6282 .iter()
6283 .map(|s| {
6284 let range = s.display_range(&display_map).sorted();
6285 let mut start = range.start.to_point(&display_map);
6286 let mut end = range.end.to_point(&display_map);
6287 start.column = 0;
6288 end.column = buffer.line_len(end.row);
6289 start..end
6290 })
6291 .collect::<Vec<_>>();
6292
6293 self.unfold_ranges(ranges, true, true, cx);
6294 }
6295
6296 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6297 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6298
6299 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6300 ..Point::new(
6301 unfold_at.buffer_row,
6302 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6303 );
6304
6305 let autoscroll = self
6306 .selections
6307 .all::<Point>(cx)
6308 .iter()
6309 .any(|selection| selection.range().overlaps(&intersection_range));
6310
6311 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6312 }
6313
6314 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6315 let selections = self.selections.all::<Point>(cx);
6316 let ranges = selections.into_iter().map(|s| s.start..s.end);
6317 self.fold_ranges(ranges, true, cx);
6318 }
6319
6320 pub fn fold_ranges<T: ToOffset + Clone>(
6321 &mut self,
6322 ranges: impl IntoIterator<Item = Range<T>>,
6323 auto_scroll: bool,
6324 cx: &mut ViewContext<Self>,
6325 ) {
6326 let mut ranges = ranges.into_iter().peekable();
6327 if ranges.peek().is_some() {
6328 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6329
6330 if auto_scroll {
6331 self.request_autoscroll(Autoscroll::fit(), cx);
6332 }
6333
6334 cx.notify();
6335 }
6336 }
6337
6338 pub fn unfold_ranges<T: ToOffset + Clone>(
6339 &mut self,
6340 ranges: impl IntoIterator<Item = Range<T>>,
6341 inclusive: bool,
6342 auto_scroll: bool,
6343 cx: &mut ViewContext<Self>,
6344 ) {
6345 let mut ranges = ranges.into_iter().peekable();
6346 if ranges.peek().is_some() {
6347 self.display_map
6348 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6349 if auto_scroll {
6350 self.request_autoscroll(Autoscroll::fit(), cx);
6351 }
6352
6353 cx.notify();
6354 }
6355 }
6356
6357 pub fn gutter_hover(
6358 &mut self,
6359 GutterHover { hovered }: &GutterHover,
6360 cx: &mut ViewContext<Self>,
6361 ) {
6362 self.gutter_hovered = *hovered;
6363 cx.notify();
6364 }
6365
6366 pub fn insert_blocks(
6367 &mut self,
6368 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6369 cx: &mut ViewContext<Self>,
6370 ) -> Vec<BlockId> {
6371 let blocks = self
6372 .display_map
6373 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6374 self.request_autoscroll(Autoscroll::fit(), cx);
6375 blocks
6376 }
6377
6378 pub fn replace_blocks(
6379 &mut self,
6380 blocks: HashMap<BlockId, RenderBlock>,
6381 cx: &mut ViewContext<Self>,
6382 ) {
6383 self.display_map
6384 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6385 self.request_autoscroll(Autoscroll::fit(), cx);
6386 }
6387
6388 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6389 self.display_map.update(cx, |display_map, cx| {
6390 display_map.remove_blocks(block_ids, cx)
6391 });
6392 }
6393
6394 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6395 self.display_map
6396 .update(cx, |map, cx| map.snapshot(cx))
6397 .longest_row()
6398 }
6399
6400 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6401 self.display_map
6402 .update(cx, |map, cx| map.snapshot(cx))
6403 .max_point()
6404 }
6405
6406 pub fn text(&self, cx: &AppContext) -> String {
6407 self.buffer.read(cx).read(cx).text()
6408 }
6409
6410 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6411 self.transact(cx, |this, cx| {
6412 this.buffer
6413 .read(cx)
6414 .as_singleton()
6415 .expect("you can only call set_text on editors for singleton buffers")
6416 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6417 });
6418 }
6419
6420 pub fn display_text(&self, cx: &mut AppContext) -> String {
6421 self.display_map
6422 .update(cx, |map, cx| map.snapshot(cx))
6423 .text()
6424 }
6425
6426 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6427 let language_name = self
6428 .buffer
6429 .read(cx)
6430 .as_singleton()
6431 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
6432 .map(|l| l.name());
6433
6434 let settings = cx.global::<Settings>();
6435 let mode = self
6436 .soft_wrap_mode_override
6437 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
6438 match mode {
6439 settings::SoftWrap::None => SoftWrap::None,
6440 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6441 settings::SoftWrap::PreferredLineLength => {
6442 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
6443 }
6444 }
6445 }
6446
6447 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
6448 self.soft_wrap_mode_override = Some(mode);
6449 cx.notify();
6450 }
6451
6452 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6453 self.display_map
6454 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6455 }
6456
6457 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6458 if self.soft_wrap_mode_override.is_some() {
6459 self.soft_wrap_mode_override.take();
6460 } else {
6461 let soft_wrap = match self.soft_wrap_mode(cx) {
6462 SoftWrap::None => settings::SoftWrap::EditorWidth,
6463 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
6464 };
6465 self.soft_wrap_mode_override = Some(soft_wrap);
6466 }
6467 cx.notify();
6468 }
6469
6470 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6471 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6472 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6473 cx.reveal_path(&file.abs_path(cx));
6474 }
6475 }
6476 }
6477
6478 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6479 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6480 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6481 if let Some(path) = file.abs_path(cx).to_str() {
6482 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6483 }
6484 }
6485 }
6486 }
6487
6488 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6489 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6490 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6491 if let Some(path) = file.path().to_str() {
6492 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6493 }
6494 }
6495 }
6496 }
6497
6498 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6499 self.highlighted_rows = rows;
6500 }
6501
6502 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6503 self.highlighted_rows.clone()
6504 }
6505
6506 pub fn highlight_background<T: 'static>(
6507 &mut self,
6508 ranges: Vec<Range<Anchor>>,
6509 color_fetcher: fn(&Theme) -> Color,
6510 cx: &mut ViewContext<Self>,
6511 ) {
6512 self.background_highlights
6513 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6514 cx.notify();
6515 }
6516
6517 #[allow(clippy::type_complexity)]
6518 pub fn clear_background_highlights<T: 'static>(
6519 &mut self,
6520 cx: &mut ViewContext<Self>,
6521 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6522 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6523 if highlights.is_some() {
6524 cx.notify();
6525 }
6526 highlights
6527 }
6528
6529 #[cfg(feature = "test-support")]
6530 pub fn all_background_highlights(
6531 &mut self,
6532 cx: &mut ViewContext<Self>,
6533 ) -> Vec<(Range<DisplayPoint>, Color)> {
6534 let snapshot = self.snapshot(cx);
6535 let buffer = &snapshot.buffer_snapshot;
6536 let start = buffer.anchor_before(0);
6537 let end = buffer.anchor_after(buffer.len());
6538 let theme = cx.global::<Settings>().theme.as_ref();
6539 self.background_highlights_in_range(start..end, &snapshot, theme)
6540 }
6541
6542 fn document_highlights_for_position<'a>(
6543 &'a self,
6544 position: Anchor,
6545 buffer: &'a MultiBufferSnapshot,
6546 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6547 let read_highlights = self
6548 .background_highlights
6549 .get(&TypeId::of::<DocumentHighlightRead>())
6550 .map(|h| &h.1);
6551 let write_highlights = self
6552 .background_highlights
6553 .get(&TypeId::of::<DocumentHighlightWrite>())
6554 .map(|h| &h.1);
6555 let left_position = position.bias_left(buffer);
6556 let right_position = position.bias_right(buffer);
6557 read_highlights
6558 .into_iter()
6559 .chain(write_highlights)
6560 .flat_map(move |ranges| {
6561 let start_ix = match ranges.binary_search_by(|probe| {
6562 let cmp = probe.end.cmp(&left_position, buffer);
6563 if cmp.is_ge() {
6564 Ordering::Greater
6565 } else {
6566 Ordering::Less
6567 }
6568 }) {
6569 Ok(i) | Err(i) => i,
6570 };
6571
6572 let right_position = right_position.clone();
6573 ranges[start_ix..]
6574 .iter()
6575 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6576 })
6577 }
6578
6579 pub fn background_highlights_in_range(
6580 &self,
6581 search_range: Range<Anchor>,
6582 display_snapshot: &DisplaySnapshot,
6583 theme: &Theme,
6584 ) -> Vec<(Range<DisplayPoint>, Color)> {
6585 let mut results = Vec::new();
6586 let buffer = &display_snapshot.buffer_snapshot;
6587 for (color_fetcher, ranges) in self.background_highlights.values() {
6588 let color = color_fetcher(theme);
6589 let start_ix = match ranges.binary_search_by(|probe| {
6590 let cmp = probe.end.cmp(&search_range.start, buffer);
6591 if cmp.is_gt() {
6592 Ordering::Greater
6593 } else {
6594 Ordering::Less
6595 }
6596 }) {
6597 Ok(i) | Err(i) => i,
6598 };
6599 for range in &ranges[start_ix..] {
6600 if range.start.cmp(&search_range.end, buffer).is_ge() {
6601 break;
6602 }
6603 let start = range
6604 .start
6605 .to_point(buffer)
6606 .to_display_point(display_snapshot);
6607 let end = range
6608 .end
6609 .to_point(buffer)
6610 .to_display_point(display_snapshot);
6611 results.push((start..end, color))
6612 }
6613 }
6614 results
6615 }
6616
6617 pub fn highlight_text<T: 'static>(
6618 &mut self,
6619 ranges: Vec<Range<Anchor>>,
6620 style: HighlightStyle,
6621 cx: &mut ViewContext<Self>,
6622 ) {
6623 self.display_map.update(cx, |map, _| {
6624 map.highlight_text(TypeId::of::<T>(), ranges, style)
6625 });
6626 cx.notify();
6627 }
6628
6629 pub fn text_highlights<'a, T: 'static>(
6630 &'a self,
6631 cx: &'a AppContext,
6632 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6633 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6634 }
6635
6636 pub fn clear_text_highlights<T: 'static>(
6637 &mut self,
6638 cx: &mut ViewContext<Self>,
6639 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6640 let highlights = self
6641 .display_map
6642 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6643 if highlights.is_some() {
6644 cx.notify();
6645 }
6646 highlights
6647 }
6648
6649 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6650 self.blink_manager.read(cx).visible() && self.focused
6651 }
6652
6653 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6654 cx.notify();
6655 }
6656
6657 fn on_buffer_event(
6658 &mut self,
6659 _: ModelHandle<MultiBuffer>,
6660 event: &multi_buffer::Event,
6661 cx: &mut ViewContext<Self>,
6662 ) {
6663 match event {
6664 multi_buffer::Event::Edited => {
6665 self.refresh_active_diagnostics(cx);
6666 self.refresh_code_actions(cx);
6667 if self.has_active_copilot_suggestion(cx) {
6668 self.update_visible_copilot_suggestion(cx);
6669 }
6670 cx.emit(Event::BufferEdited);
6671 }
6672 multi_buffer::Event::ExcerptsAdded {
6673 buffer,
6674 predecessor,
6675 excerpts,
6676 } => cx.emit(Event::ExcerptsAdded {
6677 buffer: buffer.clone(),
6678 predecessor: *predecessor,
6679 excerpts: excerpts.clone(),
6680 }),
6681 multi_buffer::Event::ExcerptsRemoved { ids } => {
6682 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6683 }
6684 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6685 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6686 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6687 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6688 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6689 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6690 multi_buffer::Event::DiagnosticsUpdated => {
6691 self.refresh_active_diagnostics(cx);
6692 }
6693 multi_buffer::Event::LanguageChanged => {}
6694 }
6695 }
6696
6697 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6698 cx.notify();
6699 }
6700
6701 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
6702 self.refresh_copilot_suggestions(true, cx);
6703 }
6704
6705 pub fn set_searchable(&mut self, searchable: bool) {
6706 self.searchable = searchable;
6707 }
6708
6709 pub fn searchable(&self) -> bool {
6710 self.searchable
6711 }
6712
6713 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6714 let active_item = workspace.active_item(cx);
6715 let editor_handle = if let Some(editor) = active_item
6716 .as_ref()
6717 .and_then(|item| item.act_as::<Self>(cx))
6718 {
6719 editor
6720 } else {
6721 cx.propagate_action();
6722 return;
6723 };
6724
6725 let editor = editor_handle.read(cx);
6726 let buffer = editor.buffer.read(cx);
6727 if buffer.is_singleton() {
6728 cx.propagate_action();
6729 return;
6730 }
6731
6732 let mut new_selections_by_buffer = HashMap::default();
6733 for selection in editor.selections.all::<usize>(cx) {
6734 for (buffer, mut range) in
6735 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6736 {
6737 if selection.reversed {
6738 mem::swap(&mut range.start, &mut range.end);
6739 }
6740 new_selections_by_buffer
6741 .entry(buffer)
6742 .or_insert(Vec::new())
6743 .push(range)
6744 }
6745 }
6746
6747 editor_handle.update(cx, |editor, cx| {
6748 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6749 });
6750 let pane = workspace.active_pane().clone();
6751 pane.update(cx, |pane, _| pane.disable_history());
6752
6753 // We defer the pane interaction because we ourselves are a workspace item
6754 // and activating a new item causes the pane to call a method on us reentrantly,
6755 // which panics if we're on the stack.
6756 cx.defer(move |workspace, cx| {
6757 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6758 let editor = workspace.open_project_item::<Self>(buffer, cx);
6759 editor.update(cx, |editor, cx| {
6760 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6761 s.select_ranges(ranges);
6762 });
6763 });
6764 }
6765
6766 pane.update(cx, |pane, _| pane.enable_history());
6767 });
6768 }
6769
6770 fn jump(
6771 workspace: &mut Workspace,
6772 path: ProjectPath,
6773 position: Point,
6774 anchor: language::Anchor,
6775 cx: &mut ViewContext<Workspace>,
6776 ) {
6777 let editor = workspace.open_path(path, None, true, cx);
6778 cx.spawn(|_, mut cx| async move {
6779 let editor = editor
6780 .await?
6781 .downcast::<Editor>()
6782 .ok_or_else(|| anyhow!("opened item was not an editor"))?
6783 .downgrade();
6784 editor.update(&mut cx, |editor, cx| {
6785 let buffer = editor
6786 .buffer()
6787 .read(cx)
6788 .as_singleton()
6789 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
6790 let buffer = buffer.read(cx);
6791 let cursor = if buffer.can_resolve(&anchor) {
6792 language::ToPoint::to_point(&anchor, buffer)
6793 } else {
6794 buffer.clip_point(position, Bias::Left)
6795 };
6796
6797 let nav_history = editor.nav_history.take();
6798 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6799 s.select_ranges([cursor..cursor]);
6800 });
6801 editor.nav_history = nav_history;
6802
6803 anyhow::Ok(())
6804 })??;
6805
6806 anyhow::Ok(())
6807 })
6808 .detach_and_log_err(cx);
6809 }
6810
6811 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6812 let snapshot = self.buffer.read(cx).read(cx);
6813 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6814 Some(
6815 ranges
6816 .iter()
6817 .map(move |range| {
6818 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6819 })
6820 .collect(),
6821 )
6822 }
6823
6824 fn selection_replacement_ranges(
6825 &self,
6826 range: Range<OffsetUtf16>,
6827 cx: &AppContext,
6828 ) -> Vec<Range<OffsetUtf16>> {
6829 let selections = self.selections.all::<OffsetUtf16>(cx);
6830 let newest_selection = selections
6831 .iter()
6832 .max_by_key(|selection| selection.id)
6833 .unwrap();
6834 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6835 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6836 let snapshot = self.buffer.read(cx).read(cx);
6837 selections
6838 .into_iter()
6839 .map(|mut selection| {
6840 selection.start.0 =
6841 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6842 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6843 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6844 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6845 })
6846 .collect()
6847 }
6848
6849 fn report_event(&self, name: &str, cx: &AppContext) {
6850 if let Some((project, file)) = self.project.as_ref().zip(
6851 self.buffer
6852 .read(cx)
6853 .as_singleton()
6854 .and_then(|b| b.read(cx).file()),
6855 ) {
6856 let settings = cx.global::<Settings>();
6857
6858 let extension = Path::new(file.file_name(cx))
6859 .extension()
6860 .and_then(|e| e.to_str());
6861 project.read(cx).client().report_event(
6862 name,
6863 json!({ "File Extension": extension, "Vim Mode": settings.vim_mode }),
6864 settings.telemetry(),
6865 );
6866 }
6867 }
6868
6869 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
6870 /// with each line being an array of {text, highlight} objects.
6871 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
6872 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
6873 return;
6874 };
6875
6876 #[derive(Serialize)]
6877 struct Chunk<'a> {
6878 text: String,
6879 highlight: Option<&'a str>,
6880 }
6881
6882 let snapshot = buffer.read(cx).snapshot();
6883 let range = self
6884 .selected_text_range(cx)
6885 .and_then(|selected_range| {
6886 if selected_range.is_empty() {
6887 None
6888 } else {
6889 Some(selected_range)
6890 }
6891 })
6892 .unwrap_or_else(|| 0..snapshot.len());
6893
6894 let chunks = snapshot.chunks(range, true);
6895 let mut lines = Vec::new();
6896 let mut line: VecDeque<Chunk> = VecDeque::new();
6897
6898 let theme = &cx.global::<Settings>().theme.editor.syntax;
6899
6900 for chunk in chunks {
6901 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
6902 let mut chunk_lines = chunk.text.split("\n").peekable();
6903 while let Some(text) = chunk_lines.next() {
6904 let mut merged_with_last_token = false;
6905 if let Some(last_token) = line.back_mut() {
6906 if last_token.highlight == highlight {
6907 last_token.text.push_str(text);
6908 merged_with_last_token = true;
6909 }
6910 }
6911
6912 if !merged_with_last_token {
6913 line.push_back(Chunk {
6914 text: text.into(),
6915 highlight,
6916 });
6917 }
6918
6919 if chunk_lines.peek().is_some() {
6920 if line.len() > 1 && line.front().unwrap().text.is_empty() {
6921 line.pop_front();
6922 }
6923 if line.len() > 1 && line.back().unwrap().text.is_empty() {
6924 line.pop_back();
6925 }
6926
6927 lines.push(mem::take(&mut line));
6928 }
6929 }
6930 }
6931
6932 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
6933 cx.write_to_clipboard(ClipboardItem::new(lines));
6934 }
6935}
6936
6937fn consume_contiguous_rows(
6938 contiguous_row_selections: &mut Vec<Selection<Point>>,
6939 selection: &Selection<Point>,
6940 display_map: &DisplaySnapshot,
6941 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
6942) -> (u32, u32) {
6943 contiguous_row_selections.push(selection.clone());
6944 let start_row = selection.start.row;
6945 let mut end_row = ending_row(selection, display_map);
6946
6947 while let Some(next_selection) = selections.peek() {
6948 if next_selection.start.row <= end_row {
6949 end_row = ending_row(next_selection, display_map);
6950 contiguous_row_selections.push(selections.next().unwrap().clone());
6951 } else {
6952 break;
6953 }
6954 }
6955 (start_row, end_row)
6956}
6957
6958fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
6959 if next_selection.end.column > 0 || next_selection.is_empty() {
6960 display_map.next_line_boundary(next_selection.end).0.row + 1
6961 } else {
6962 next_selection.end.row
6963 }
6964}
6965
6966impl EditorSnapshot {
6967 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6968 self.display_snapshot.buffer_snapshot.language_at(position)
6969 }
6970
6971 pub fn is_focused(&self) -> bool {
6972 self.is_focused
6973 }
6974
6975 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6976 self.placeholder_text.as_ref()
6977 }
6978
6979 pub fn scroll_position(&self) -> Vector2F {
6980 self.scroll_anchor.scroll_position(&self.display_snapshot)
6981 }
6982}
6983
6984impl Deref for EditorSnapshot {
6985 type Target = DisplaySnapshot;
6986
6987 fn deref(&self) -> &Self::Target {
6988 &self.display_snapshot
6989 }
6990}
6991
6992#[derive(Clone, Debug, PartialEq, Eq)]
6993pub enum Event {
6994 InputIgnored {
6995 text: Arc<str>,
6996 },
6997 ExcerptsAdded {
6998 buffer: ModelHandle<Buffer>,
6999 predecessor: ExcerptId,
7000 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7001 },
7002 ExcerptsRemoved {
7003 ids: Vec<ExcerptId>,
7004 },
7005 BufferEdited,
7006 Edited,
7007 Reparsed,
7008 Blurred,
7009 DirtyChanged,
7010 Saved,
7011 TitleChanged,
7012 SelectionsChanged {
7013 local: bool,
7014 },
7015 ScrollPositionChanged {
7016 local: bool,
7017 },
7018 Closed,
7019}
7020
7021pub struct EditorFocused(pub ViewHandle<Editor>);
7022pub struct EditorBlurred(pub ViewHandle<Editor>);
7023pub struct EditorReleased(pub WeakViewHandle<Editor>);
7024
7025impl Entity for Editor {
7026 type Event = Event;
7027
7028 fn release(&mut self, cx: &mut AppContext) {
7029 cx.emit_global(EditorReleased(self.handle.clone()));
7030 }
7031}
7032
7033impl View for Editor {
7034 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7035 let style = self.style(cx);
7036 let font_changed = self.display_map.update(cx, |map, cx| {
7037 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7038 map.set_font(style.text.font_id, style.text.font_size, cx)
7039 });
7040
7041 if font_changed {
7042 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7043 hide_hover(editor, cx);
7044 hide_link_definition(editor, cx);
7045 });
7046 }
7047
7048 Stack::new()
7049 .with_child(EditorElement::new(style.clone()))
7050 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7051 .into_any()
7052 }
7053
7054 fn ui_name() -> &'static str {
7055 "Editor"
7056 }
7057
7058 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7059 if cx.is_self_focused() {
7060 let focused_event = EditorFocused(cx.handle());
7061 cx.emit_global(focused_event);
7062 }
7063 if let Some(rename) = self.pending_rename.as_ref() {
7064 cx.focus(&rename.editor);
7065 } else {
7066 if !self.focused {
7067 self.blink_manager.update(cx, BlinkManager::enable);
7068 }
7069 self.focused = true;
7070 self.buffer.update(cx, |buffer, cx| {
7071 buffer.finalize_last_transaction(cx);
7072 if self.leader_replica_id.is_none() {
7073 buffer.set_active_selections(
7074 &self.selections.disjoint_anchors(),
7075 self.selections.line_mode,
7076 self.cursor_shape,
7077 cx,
7078 );
7079 }
7080 });
7081 }
7082 }
7083
7084 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7085 let blurred_event = EditorBlurred(cx.handle());
7086 cx.emit_global(blurred_event);
7087 self.focused = false;
7088 self.blink_manager.update(cx, BlinkManager::disable);
7089 self.buffer
7090 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7091 self.hide_context_menu(cx);
7092 hide_hover(self, cx);
7093 cx.emit(Event::Blurred);
7094 cx.notify();
7095 }
7096
7097 fn modifiers_changed(
7098 &mut self,
7099 event: &gpui::platform::ModifiersChangedEvent,
7100 cx: &mut ViewContext<Self>,
7101 ) -> bool {
7102 let pending_selection = self.has_pending_selection();
7103
7104 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7105 if event.cmd && !pending_selection {
7106 let snapshot = self.snapshot(cx);
7107 let kind = if event.shift {
7108 LinkDefinitionKind::Type
7109 } else {
7110 LinkDefinitionKind::Symbol
7111 };
7112
7113 show_link_definition(kind, self, point, snapshot, cx);
7114 return false;
7115 }
7116 }
7117
7118 {
7119 if self.link_go_to_definition_state.symbol_range.is_some()
7120 || !self.link_go_to_definition_state.definitions.is_empty()
7121 {
7122 self.link_go_to_definition_state.symbol_range.take();
7123 self.link_go_to_definition_state.definitions.clear();
7124 cx.notify();
7125 }
7126
7127 self.link_go_to_definition_state.task = None;
7128
7129 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7130 }
7131
7132 false
7133 }
7134
7135 fn keymap_context(&self, _: &AppContext) -> KeymapContext {
7136 let mut context = Self::default_keymap_context();
7137 let mode = match self.mode {
7138 EditorMode::SingleLine => "single_line",
7139 EditorMode::AutoHeight { .. } => "auto_height",
7140 EditorMode::Full => "full",
7141 };
7142 context.add_key("mode", mode);
7143 if self.pending_rename.is_some() {
7144 context.add_identifier("renaming");
7145 }
7146 match self.context_menu.as_ref() {
7147 Some(ContextMenu::Completions(_)) => context.add_identifier("showing_completions"),
7148 Some(ContextMenu::CodeActions(_)) => context.add_identifier("showing_code_actions"),
7149 None => {}
7150 }
7151
7152 for layer in self.keymap_context_layers.values() {
7153 context.extend(layer);
7154 }
7155
7156 context
7157 }
7158
7159 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7160 Some(
7161 self.buffer
7162 .read(cx)
7163 .read(cx)
7164 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7165 .collect(),
7166 )
7167 }
7168
7169 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7170 // Prevent the IME menu from appearing when holding down an alphabetic key
7171 // while input is disabled.
7172 if !self.input_enabled {
7173 return None;
7174 }
7175
7176 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7177 Some(range.start.0..range.end.0)
7178 }
7179
7180 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7181 let snapshot = self.buffer.read(cx).read(cx);
7182 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7183 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7184 }
7185
7186 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7187 self.clear_text_highlights::<InputComposition>(cx);
7188 self.ime_transaction.take();
7189 }
7190
7191 fn replace_text_in_range(
7192 &mut self,
7193 range_utf16: Option<Range<usize>>,
7194 text: &str,
7195 cx: &mut ViewContext<Self>,
7196 ) {
7197 self.transact(cx, |this, cx| {
7198 if this.input_enabled {
7199 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7200 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7201 Some(this.selection_replacement_ranges(range_utf16, cx))
7202 } else {
7203 this.marked_text_ranges(cx)
7204 };
7205
7206 if let Some(new_selected_ranges) = new_selected_ranges {
7207 this.change_selections(None, cx, |selections| {
7208 selections.select_ranges(new_selected_ranges)
7209 });
7210 }
7211 }
7212
7213 this.handle_input(text, cx);
7214 });
7215
7216 if !self.input_enabled {
7217 return;
7218 }
7219
7220 if let Some(transaction) = self.ime_transaction {
7221 self.buffer.update(cx, |buffer, cx| {
7222 buffer.group_until_transaction(transaction, cx);
7223 });
7224 }
7225
7226 self.unmark_text(cx);
7227 }
7228
7229 fn replace_and_mark_text_in_range(
7230 &mut self,
7231 range_utf16: Option<Range<usize>>,
7232 text: &str,
7233 new_selected_range_utf16: Option<Range<usize>>,
7234 cx: &mut ViewContext<Self>,
7235 ) {
7236 if !self.input_enabled {
7237 return;
7238 }
7239
7240 let transaction = self.transact(cx, |this, cx| {
7241 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7242 let snapshot = this.buffer.read(cx).read(cx);
7243 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7244 for marked_range in &mut marked_ranges {
7245 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7246 marked_range.start.0 += relative_range_utf16.start;
7247 marked_range.start =
7248 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7249 marked_range.end =
7250 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7251 }
7252 }
7253 Some(marked_ranges)
7254 } else if let Some(range_utf16) = range_utf16 {
7255 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7256 Some(this.selection_replacement_ranges(range_utf16, cx))
7257 } else {
7258 None
7259 };
7260
7261 if let Some(ranges) = ranges_to_replace {
7262 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7263 }
7264
7265 let marked_ranges = {
7266 let snapshot = this.buffer.read(cx).read(cx);
7267 this.selections
7268 .disjoint_anchors()
7269 .iter()
7270 .map(|selection| {
7271 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7272 })
7273 .collect::<Vec<_>>()
7274 };
7275
7276 if text.is_empty() {
7277 this.unmark_text(cx);
7278 } else {
7279 this.highlight_text::<InputComposition>(
7280 marked_ranges.clone(),
7281 this.style(cx).composition_mark,
7282 cx,
7283 );
7284 }
7285
7286 this.handle_input(text, cx);
7287
7288 if let Some(new_selected_range) = new_selected_range_utf16 {
7289 let snapshot = this.buffer.read(cx).read(cx);
7290 let new_selected_ranges = marked_ranges
7291 .into_iter()
7292 .map(|marked_range| {
7293 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7294 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7295 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7296 snapshot.clip_offset_utf16(new_start, Bias::Left)
7297 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7298 })
7299 .collect::<Vec<_>>();
7300
7301 drop(snapshot);
7302 this.change_selections(None, cx, |selections| {
7303 selections.select_ranges(new_selected_ranges)
7304 });
7305 }
7306 });
7307
7308 self.ime_transaction = self.ime_transaction.or(transaction);
7309 if let Some(transaction) = self.ime_transaction {
7310 self.buffer.update(cx, |buffer, cx| {
7311 buffer.group_until_transaction(transaction, cx);
7312 });
7313 }
7314
7315 if self.text_highlights::<InputComposition>(cx).is_none() {
7316 self.ime_transaction.take();
7317 }
7318 }
7319}
7320
7321fn build_style(
7322 settings: &Settings,
7323 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7324 override_text_style: Option<&OverrideTextStyle>,
7325 cx: &AppContext,
7326) -> EditorStyle {
7327 let font_cache = cx.font_cache();
7328
7329 let theme_id = settings.theme.meta.id;
7330 let mut theme = settings.theme.editor.clone();
7331 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7332 let field_editor_theme = get_field_editor_theme(&settings.theme);
7333 theme.text_color = field_editor_theme.text.color;
7334 theme.selection = field_editor_theme.selection;
7335 theme.background = field_editor_theme
7336 .container
7337 .background_color
7338 .unwrap_or_default();
7339 EditorStyle {
7340 text: field_editor_theme.text,
7341 placeholder_text: field_editor_theme.placeholder_text,
7342 theme,
7343 theme_id,
7344 }
7345 } else {
7346 let font_family_id = settings.buffer_font_family;
7347 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7348 let font_properties = Default::default();
7349 let font_id = font_cache
7350 .select_font(font_family_id, &font_properties)
7351 .unwrap();
7352 let font_size = settings.buffer_font_size;
7353 EditorStyle {
7354 text: TextStyle {
7355 color: settings.theme.editor.text_color,
7356 font_family_name,
7357 font_family_id,
7358 font_id,
7359 font_size,
7360 font_properties,
7361 underline: Default::default(),
7362 },
7363 placeholder_text: None,
7364 theme,
7365 theme_id,
7366 }
7367 };
7368
7369 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7370 if let Some(highlighted) = style
7371 .text
7372 .clone()
7373 .highlight(highlight_style, font_cache)
7374 .log_err()
7375 {
7376 style.text = highlighted;
7377 }
7378 }
7379
7380 style
7381}
7382
7383trait SelectionExt {
7384 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7385 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7386 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7387 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7388 -> Range<u32>;
7389}
7390
7391impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7392 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7393 let start = self.start.to_point(buffer);
7394 let end = self.end.to_point(buffer);
7395 if self.reversed {
7396 end..start
7397 } else {
7398 start..end
7399 }
7400 }
7401
7402 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7403 let start = self.start.to_offset(buffer);
7404 let end = self.end.to_offset(buffer);
7405 if self.reversed {
7406 end..start
7407 } else {
7408 start..end
7409 }
7410 }
7411
7412 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7413 let start = self
7414 .start
7415 .to_point(&map.buffer_snapshot)
7416 .to_display_point(map);
7417 let end = self
7418 .end
7419 .to_point(&map.buffer_snapshot)
7420 .to_display_point(map);
7421 if self.reversed {
7422 end..start
7423 } else {
7424 start..end
7425 }
7426 }
7427
7428 fn spanned_rows(
7429 &self,
7430 include_end_if_at_line_start: bool,
7431 map: &DisplaySnapshot,
7432 ) -> Range<u32> {
7433 let start = self.start.to_point(&map.buffer_snapshot);
7434 let mut end = self.end.to_point(&map.buffer_snapshot);
7435 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7436 end.row -= 1;
7437 }
7438
7439 let buffer_start = map.prev_line_boundary(start).0;
7440 let buffer_end = map.next_line_boundary(end).0;
7441 buffer_start.row..buffer_end.row + 1
7442 }
7443}
7444
7445impl<T: InvalidationRegion> InvalidationStack<T> {
7446 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7447 where
7448 S: Clone + ToOffset,
7449 {
7450 while let Some(region) = self.last() {
7451 let all_selections_inside_invalidation_ranges =
7452 if selections.len() == region.ranges().len() {
7453 selections
7454 .iter()
7455 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7456 .all(|(selection, invalidation_range)| {
7457 let head = selection.head().to_offset(buffer);
7458 invalidation_range.start <= head && invalidation_range.end >= head
7459 })
7460 } else {
7461 false
7462 };
7463
7464 if all_selections_inside_invalidation_ranges {
7465 break;
7466 } else {
7467 self.pop();
7468 }
7469 }
7470 }
7471}
7472
7473impl<T> Default for InvalidationStack<T> {
7474 fn default() -> Self {
7475 Self(Default::default())
7476 }
7477}
7478
7479impl<T> Deref for InvalidationStack<T> {
7480 type Target = Vec<T>;
7481
7482 fn deref(&self) -> &Self::Target {
7483 &self.0
7484 }
7485}
7486
7487impl<T> DerefMut for InvalidationStack<T> {
7488 fn deref_mut(&mut self) -> &mut Self::Target {
7489 &mut self.0
7490 }
7491}
7492
7493impl InvalidationRegion for SnippetState {
7494 fn ranges(&self) -> &[Range<Anchor>] {
7495 &self.ranges[self.active_index]
7496 }
7497}
7498
7499impl Deref for EditorStyle {
7500 type Target = theme::Editor;
7501
7502 fn deref(&self) -> &Self::Target {
7503 &self.theme
7504 }
7505}
7506
7507pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7508 let mut highlighted_lines = Vec::new();
7509 for line in diagnostic.message.lines() {
7510 highlighted_lines.push(highlight_diagnostic_message(line));
7511 }
7512
7513 Arc::new(move |cx: &mut BlockContext| {
7514 let settings = cx.global::<Settings>();
7515 let theme = &settings.theme.editor;
7516 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7517 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
7518 Flex::column()
7519 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7520 Label::new(
7521 line.clone(),
7522 style.message.clone().with_font_size(font_size),
7523 )
7524 .with_highlights(highlights.clone())
7525 .contained()
7526 .with_margin_left(cx.anchor_x)
7527 }))
7528 .aligned()
7529 .left()
7530 .into_any()
7531 })
7532}
7533
7534pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
7535 let mut message_without_backticks = String::new();
7536 let mut prev_offset = 0;
7537 let mut inside_block = false;
7538 let mut highlights = Vec::new();
7539 for (match_ix, (offset, _)) in message
7540 .match_indices('`')
7541 .chain([(message.len(), "")])
7542 .enumerate()
7543 {
7544 message_without_backticks.push_str(&message[prev_offset..offset]);
7545 if inside_block {
7546 highlights.extend(prev_offset - match_ix..offset - match_ix);
7547 }
7548
7549 inside_block = !inside_block;
7550 prev_offset = offset + 1;
7551 }
7552
7553 (message_without_backticks, highlights)
7554}
7555
7556pub fn diagnostic_style(
7557 severity: DiagnosticSeverity,
7558 valid: bool,
7559 theme: &theme::Editor,
7560) -> DiagnosticStyle {
7561 match (severity, valid) {
7562 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7563 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7564 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7565 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
7566 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
7567 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
7568 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
7569 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
7570 _ => theme.invalid_hint_diagnostic.clone(),
7571 }
7572}
7573
7574pub fn combine_syntax_and_fuzzy_match_highlights(
7575 text: &str,
7576 default_style: HighlightStyle,
7577 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
7578 match_indices: &[usize],
7579) -> Vec<(Range<usize>, HighlightStyle)> {
7580 let mut result = Vec::new();
7581 let mut match_indices = match_indices.iter().copied().peekable();
7582
7583 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
7584 {
7585 syntax_highlight.weight = None;
7586
7587 // Add highlights for any fuzzy match characters before the next
7588 // syntax highlight range.
7589 while let Some(&match_index) = match_indices.peek() {
7590 if match_index >= range.start {
7591 break;
7592 }
7593 match_indices.next();
7594 let end_index = char_ix_after(match_index, text);
7595 let mut match_style = default_style;
7596 match_style.weight = Some(fonts::Weight::BOLD);
7597 result.push((match_index..end_index, match_style));
7598 }
7599
7600 if range.start == usize::MAX {
7601 break;
7602 }
7603
7604 // Add highlights for any fuzzy match characters within the
7605 // syntax highlight range.
7606 let mut offset = range.start;
7607 while let Some(&match_index) = match_indices.peek() {
7608 if match_index >= range.end {
7609 break;
7610 }
7611
7612 match_indices.next();
7613 if match_index > offset {
7614 result.push((offset..match_index, syntax_highlight));
7615 }
7616
7617 let mut end_index = char_ix_after(match_index, text);
7618 while let Some(&next_match_index) = match_indices.peek() {
7619 if next_match_index == end_index && next_match_index < range.end {
7620 end_index = char_ix_after(next_match_index, text);
7621 match_indices.next();
7622 } else {
7623 break;
7624 }
7625 }
7626
7627 let mut match_style = syntax_highlight;
7628 match_style.weight = Some(fonts::Weight::BOLD);
7629 result.push((match_index..end_index, match_style));
7630 offset = end_index;
7631 }
7632
7633 if offset < range.end {
7634 result.push((offset..range.end, syntax_highlight));
7635 }
7636 }
7637
7638 fn char_ix_after(ix: usize, text: &str) -> usize {
7639 ix + text[ix..].chars().next().unwrap().len_utf8()
7640 }
7641
7642 result
7643}
7644
7645pub fn styled_runs_for_code_label<'a>(
7646 label: &'a CodeLabel,
7647 syntax_theme: &'a theme::SyntaxTheme,
7648) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
7649 let fade_out = HighlightStyle {
7650 fade_out: Some(0.35),
7651 ..Default::default()
7652 };
7653
7654 let mut prev_end = label.filter_range.end;
7655 label
7656 .runs
7657 .iter()
7658 .enumerate()
7659 .flat_map(move |(ix, (range, highlight_id))| {
7660 let style = if let Some(style) = highlight_id.style(syntax_theme) {
7661 style
7662 } else {
7663 return Default::default();
7664 };
7665 let mut muted_style = style;
7666 muted_style.highlight(fade_out);
7667
7668 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
7669 if range.start >= label.filter_range.end {
7670 if range.start > prev_end {
7671 runs.push((prev_end..range.start, fade_out));
7672 }
7673 runs.push((range.clone(), muted_style));
7674 } else if range.end <= label.filter_range.end {
7675 runs.push((range.clone(), style));
7676 } else {
7677 runs.push((range.start..label.filter_range.end, style));
7678 runs.push((label.filter_range.end..range.end, muted_style));
7679 }
7680 prev_end = cmp::max(prev_end, range.end);
7681
7682 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
7683 runs.push((prev_end..label.text.len(), fade_out));
7684 }
7685
7686 runs
7687 })
7688}
7689
7690pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
7691 let mut index = 0;
7692 let mut codepoints = text.char_indices().peekable();
7693
7694 std::iter::from_fn(move || {
7695 let start_index = index;
7696 while let Some((new_index, codepoint)) = codepoints.next() {
7697 index = new_index + codepoint.len_utf8();
7698 let current_upper = codepoint.is_uppercase();
7699 let next_upper = codepoints
7700 .peek()
7701 .map(|(_, c)| c.is_uppercase())
7702 .unwrap_or(false);
7703
7704 if !current_upper && next_upper {
7705 return Some(&text[start_index..index]);
7706 }
7707 }
7708
7709 index = text.len();
7710 if start_index < text.len() {
7711 return Some(&text[start_index..]);
7712 }
7713 None
7714 })
7715 .flat_map(|word| word.split_inclusive('_'))
7716}
7717
7718trait RangeToAnchorExt {
7719 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
7720}
7721
7722impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7723 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7724 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7725 }
7726}