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