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