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