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