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