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 settings::get::<EditorSettings>(cx).use_on_type_format {
2127 if let Some(on_type_format_task) =
2128 this.trigger_on_type_formatting(text.to_string(), cx)
2129 {
2130 on_type_format_task.detach_and_log_err(cx);
2131 }
2132 }
2133
2134 if had_active_copilot_suggestion {
2135 this.refresh_copilot_suggestions(true, cx);
2136 if !this.has_active_copilot_suggestion(cx) {
2137 this.trigger_completion_on_input(&text, cx);
2138 }
2139 } else {
2140 this.trigger_completion_on_input(&text, cx);
2141 this.refresh_copilot_suggestions(true, cx);
2142 }
2143 });
2144 }
2145
2146 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2147 self.transact(cx, |this, cx| {
2148 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2149 let selections = this.selections.all::<usize>(cx);
2150
2151 let buffer = this.buffer.read(cx).snapshot(cx);
2152 selections
2153 .iter()
2154 .map(|selection| {
2155 let start_point = selection.start.to_point(&buffer);
2156 let mut indent = buffer.indent_size_for_line(start_point.row);
2157 indent.len = cmp::min(indent.len, start_point.column);
2158 let start = selection.start;
2159 let end = selection.end;
2160
2161 let mut insert_extra_newline = false;
2162 if let Some(language) = buffer.language_scope_at(start) {
2163 let leading_whitespace_len = buffer
2164 .reversed_chars_at(start)
2165 .take_while(|c| c.is_whitespace() && *c != '\n')
2166 .map(|c| c.len_utf8())
2167 .sum::<usize>();
2168
2169 let trailing_whitespace_len = buffer
2170 .chars_at(end)
2171 .take_while(|c| c.is_whitespace() && *c != '\n')
2172 .map(|c| c.len_utf8())
2173 .sum::<usize>();
2174
2175 insert_extra_newline = language.brackets().any(|(pair, enabled)| {
2176 let pair_start = pair.start.trim_end();
2177 let pair_end = pair.end.trim_start();
2178
2179 enabled
2180 && pair.newline
2181 && buffer
2182 .contains_str_at(end + trailing_whitespace_len, pair_end)
2183 && buffer.contains_str_at(
2184 (start - leading_whitespace_len)
2185 .saturating_sub(pair_start.len()),
2186 pair_start,
2187 )
2188 });
2189 }
2190
2191 let mut new_text = String::with_capacity(1 + indent.len as usize);
2192 new_text.push('\n');
2193 new_text.extend(indent.chars());
2194 if insert_extra_newline {
2195 new_text = new_text.repeat(2);
2196 }
2197
2198 let anchor = buffer.anchor_after(end);
2199 let new_selection = selection.map(|_| anchor);
2200 (
2201 (start..end, new_text),
2202 (insert_extra_newline, new_selection),
2203 )
2204 })
2205 .unzip()
2206 };
2207
2208 this.edit_with_autoindent(edits, cx);
2209 let buffer = this.buffer.read(cx).snapshot(cx);
2210 let new_selections = selection_fixup_info
2211 .into_iter()
2212 .map(|(extra_newline_inserted, new_selection)| {
2213 let mut cursor = new_selection.end.to_point(&buffer);
2214 if extra_newline_inserted {
2215 cursor.row -= 1;
2216 cursor.column = buffer.line_len(cursor.row);
2217 }
2218 new_selection.map(|_| cursor)
2219 })
2220 .collect();
2221
2222 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2223 this.refresh_copilot_suggestions(true, cx);
2224 });
2225 }
2226
2227 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2228 let buffer = self.buffer.read(cx);
2229 let snapshot = buffer.snapshot(cx);
2230
2231 let mut edits = Vec::new();
2232 let mut rows = Vec::new();
2233 let mut rows_inserted = 0;
2234
2235 for selection in self.selections.all_adjusted(cx) {
2236 let cursor = selection.head();
2237 let row = cursor.row;
2238
2239 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2240
2241 let newline = "\n".to_string();
2242 edits.push((start_of_line..start_of_line, newline));
2243
2244 rows.push(row + rows_inserted);
2245 rows_inserted += 1;
2246 }
2247
2248 self.transact(cx, |editor, cx| {
2249 editor.edit(edits, cx);
2250
2251 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2252 let mut index = 0;
2253 s.move_cursors_with(|map, _, _| {
2254 let row = rows[index];
2255 index += 1;
2256
2257 let point = Point::new(row, 0);
2258 let boundary = map.next_line_boundary(point).1;
2259 let clipped = map.clip_point(boundary, Bias::Left);
2260
2261 (clipped, SelectionGoal::None)
2262 });
2263 });
2264
2265 let mut indent_edits = Vec::new();
2266 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2267 for row in rows {
2268 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2269 for (row, indent) in indents {
2270 if indent.len == 0 {
2271 continue;
2272 }
2273
2274 let text = match indent.kind {
2275 IndentKind::Space => " ".repeat(indent.len as usize),
2276 IndentKind::Tab => "\t".repeat(indent.len as usize),
2277 };
2278 let point = Point::new(row, 0);
2279 indent_edits.push((point..point, text));
2280 }
2281 }
2282 editor.edit(indent_edits, cx);
2283 });
2284 }
2285
2286 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2287 let buffer = self.buffer.read(cx);
2288 let snapshot = buffer.snapshot(cx);
2289
2290 let mut edits = Vec::new();
2291 let mut rows = Vec::new();
2292 let mut rows_inserted = 0;
2293
2294 for selection in self.selections.all_adjusted(cx) {
2295 let cursor = selection.head();
2296 let row = cursor.row;
2297
2298 let point = Point::new(row + 1, 0);
2299 let start_of_line = snapshot.clip_point(point, Bias::Left);
2300
2301 let newline = "\n".to_string();
2302 edits.push((start_of_line..start_of_line, newline));
2303
2304 rows_inserted += 1;
2305 rows.push(row + rows_inserted);
2306 }
2307
2308 self.transact(cx, |editor, cx| {
2309 editor.edit(edits, cx);
2310
2311 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2312 let mut index = 0;
2313 s.move_cursors_with(|map, _, _| {
2314 let row = rows[index];
2315 index += 1;
2316
2317 let point = Point::new(row, 0);
2318 let boundary = map.next_line_boundary(point).1;
2319 let clipped = map.clip_point(boundary, Bias::Left);
2320
2321 (clipped, SelectionGoal::None)
2322 });
2323 });
2324
2325 let mut indent_edits = Vec::new();
2326 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2327 for row in rows {
2328 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2329 for (row, indent) in indents {
2330 if indent.len == 0 {
2331 continue;
2332 }
2333
2334 let text = match indent.kind {
2335 IndentKind::Space => " ".repeat(indent.len as usize),
2336 IndentKind::Tab => "\t".repeat(indent.len as usize),
2337 };
2338 let point = Point::new(row, 0);
2339 indent_edits.push((point..point, text));
2340 }
2341 }
2342 editor.edit(indent_edits, cx);
2343 });
2344 }
2345
2346 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2347 self.insert_with_autoindent_mode(
2348 text,
2349 Some(AutoindentMode::Block {
2350 original_indent_columns: Vec::new(),
2351 }),
2352 cx,
2353 );
2354 }
2355
2356 fn insert_with_autoindent_mode(
2357 &mut self,
2358 text: &str,
2359 autoindent_mode: Option<AutoindentMode>,
2360 cx: &mut ViewContext<Self>,
2361 ) {
2362 if self.read_only {
2363 return;
2364 }
2365
2366 let text: Arc<str> = text.into();
2367 self.transact(cx, |this, cx| {
2368 let old_selections = this.selections.all_adjusted(cx);
2369 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2370 let anchors = {
2371 let snapshot = buffer.read(cx);
2372 old_selections
2373 .iter()
2374 .map(|s| {
2375 let anchor = snapshot.anchor_after(s.end);
2376 s.map(|_| anchor)
2377 })
2378 .collect::<Vec<_>>()
2379 };
2380 buffer.edit(
2381 old_selections
2382 .iter()
2383 .map(|s| (s.start..s.end, text.clone())),
2384 autoindent_mode,
2385 cx,
2386 );
2387 anchors
2388 });
2389
2390 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2391 s.select_anchors(selection_anchors);
2392 })
2393 });
2394 }
2395
2396 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2397 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2398 return;
2399 }
2400
2401 let selection = self.selections.newest_anchor();
2402 if self
2403 .buffer
2404 .read(cx)
2405 .is_completion_trigger(selection.head(), text, cx)
2406 {
2407 self.show_completions(&ShowCompletions, cx);
2408 } else {
2409 self.hide_context_menu(cx);
2410 }
2411 }
2412
2413 /// If any empty selections is touching the start of its innermost containing autoclose
2414 /// region, expand it to select the brackets.
2415 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2416 let selections = self.selections.all::<usize>(cx);
2417 let buffer = self.buffer.read(cx).read(cx);
2418 let mut new_selections = Vec::new();
2419 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2420 if let (Some(region), true) = (region, selection.is_empty()) {
2421 let mut range = region.range.to_offset(&buffer);
2422 if selection.start == range.start {
2423 if range.start >= region.pair.start.len() {
2424 range.start -= region.pair.start.len();
2425 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2426 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2427 range.end += region.pair.end.len();
2428 selection.start = range.start;
2429 selection.end = range.end;
2430 }
2431 }
2432 }
2433 }
2434 }
2435 new_selections.push(selection);
2436 }
2437
2438 drop(buffer);
2439 self.change_selections(None, cx, |selections| selections.select(new_selections));
2440 }
2441
2442 /// Iterate the given selections, and for each one, find the smallest surrounding
2443 /// autoclose region. This uses the ordering of the selections and the autoclose
2444 /// regions to avoid repeated comparisons.
2445 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2446 &'a self,
2447 selections: impl IntoIterator<Item = Selection<D>>,
2448 buffer: &'a MultiBufferSnapshot,
2449 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2450 let mut i = 0;
2451 let mut regions = self.autoclose_regions.as_slice();
2452 selections.into_iter().map(move |selection| {
2453 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2454
2455 let mut enclosing = None;
2456 while let Some(pair_state) = regions.get(i) {
2457 if pair_state.range.end.to_offset(buffer) < range.start {
2458 regions = ®ions[i + 1..];
2459 i = 0;
2460 } else if pair_state.range.start.to_offset(buffer) > range.end {
2461 break;
2462 } else if pair_state.selection_id == selection.id {
2463 enclosing = Some(pair_state);
2464 i += 1;
2465 }
2466 }
2467
2468 (selection.clone(), enclosing)
2469 })
2470 }
2471
2472 /// Remove any autoclose regions that no longer contain their selection.
2473 fn invalidate_autoclose_regions(
2474 &mut self,
2475 mut selections: &[Selection<Anchor>],
2476 buffer: &MultiBufferSnapshot,
2477 ) {
2478 self.autoclose_regions.retain(|state| {
2479 let mut i = 0;
2480 while let Some(selection) = selections.get(i) {
2481 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2482 selections = &selections[1..];
2483 continue;
2484 }
2485 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2486 break;
2487 }
2488 if selection.id == state.selection_id {
2489 return true;
2490 } else {
2491 i += 1;
2492 }
2493 }
2494 false
2495 });
2496 }
2497
2498 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2499 let offset = position.to_offset(buffer);
2500 let (word_range, kind) = buffer.surrounding_word(offset);
2501 if offset > word_range.start && kind == Some(CharKind::Word) {
2502 Some(
2503 buffer
2504 .text_for_range(word_range.start..offset)
2505 .collect::<String>(),
2506 )
2507 } else {
2508 None
2509 }
2510 }
2511
2512 fn trigger_on_type_formatting(
2513 &self,
2514 input: String,
2515 cx: &mut ViewContext<Self>,
2516 ) -> Option<Task<Result<()>>> {
2517 if input.len() != 1 {
2518 return None;
2519 }
2520
2521 let project = self.project.as_ref()?;
2522 let position = self.selections.newest_anchor().head();
2523 let (buffer, buffer_position) = self
2524 .buffer
2525 .read(cx)
2526 .text_anchor_for_position(position.clone(), cx)?;
2527 let on_type_formatting = project.update(cx, |project, cx| {
2528 project.on_type_format(buffer, buffer_position, input, true, cx)
2529 });
2530
2531 Some(cx.spawn(|editor, mut cx| async move {
2532 on_type_formatting.await?;
2533 editor.update(&mut cx, |editor, cx| {
2534 editor.refresh_document_highlights(cx);
2535 })?;
2536 Ok(())
2537 }))
2538 }
2539
2540 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2541 if self.pending_rename.is_some() {
2542 return;
2543 }
2544
2545 let project = if let Some(project) = self.project.clone() {
2546 project
2547 } else {
2548 return;
2549 };
2550
2551 let position = self.selections.newest_anchor().head();
2552 let (buffer, buffer_position) = if let Some(output) = self
2553 .buffer
2554 .read(cx)
2555 .text_anchor_for_position(position.clone(), cx)
2556 {
2557 output
2558 } else {
2559 return;
2560 };
2561
2562 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2563 let completions = project.update(cx, |project, cx| {
2564 project.completions(&buffer, buffer_position, cx)
2565 });
2566
2567 let id = post_inc(&mut self.next_completion_id);
2568 let task = cx.spawn(|this, mut cx| {
2569 async move {
2570 let menu = if let Some(completions) = completions.await.log_err() {
2571 let mut menu = CompletionsMenu {
2572 id,
2573 initial_position: position,
2574 match_candidates: completions
2575 .iter()
2576 .enumerate()
2577 .map(|(id, completion)| {
2578 StringMatchCandidate::new(
2579 id,
2580 completion.label.text[completion.label.filter_range.clone()]
2581 .into(),
2582 )
2583 })
2584 .collect(),
2585 buffer,
2586 completions: completions.into(),
2587 matches: Vec::new().into(),
2588 selected_item: 0,
2589 list: Default::default(),
2590 };
2591 menu.filter(query.as_deref(), cx.background()).await;
2592 if menu.matches.is_empty() {
2593 None
2594 } else {
2595 Some(menu)
2596 }
2597 } else {
2598 None
2599 };
2600
2601 this.update(&mut cx, |this, cx| {
2602 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2603
2604 match this.context_menu.as_ref() {
2605 None => {}
2606 Some(ContextMenu::Completions(prev_menu)) => {
2607 if prev_menu.id > id {
2608 return;
2609 }
2610 }
2611 _ => return,
2612 }
2613
2614 if this.focused && menu.is_some() {
2615 let menu = menu.unwrap();
2616 this.show_context_menu(ContextMenu::Completions(menu), cx);
2617 } else if this.completion_tasks.is_empty() {
2618 // If there are no more completion tasks and the last menu was
2619 // empty, we should hide it. If it was already hidden, we should
2620 // also show the copilot suggestion when available.
2621 if this.hide_context_menu(cx).is_none() {
2622 this.update_visible_copilot_suggestion(cx);
2623 }
2624 }
2625 })?;
2626
2627 Ok::<_, anyhow::Error>(())
2628 }
2629 .log_err()
2630 });
2631 self.completion_tasks.push((id, task));
2632 }
2633
2634 pub fn confirm_completion(
2635 &mut self,
2636 action: &ConfirmCompletion,
2637 cx: &mut ViewContext<Self>,
2638 ) -> Option<Task<Result<()>>> {
2639 use language::ToOffset as _;
2640
2641 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2642 menu
2643 } else {
2644 return None;
2645 };
2646
2647 let mat = completions_menu
2648 .matches
2649 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2650 let buffer_handle = completions_menu.buffer;
2651 let completion = completions_menu.completions.get(mat.candidate_id)?;
2652
2653 let snippet;
2654 let text;
2655 if completion.is_snippet() {
2656 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2657 text = snippet.as_ref().unwrap().text.clone();
2658 } else {
2659 snippet = None;
2660 text = completion.new_text.clone();
2661 };
2662 let selections = self.selections.all::<usize>(cx);
2663 let buffer = buffer_handle.read(cx);
2664 let old_range = completion.old_range.to_offset(buffer);
2665 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2666
2667 let newest_selection = self.selections.newest_anchor();
2668 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2669 return None;
2670 }
2671
2672 let lookbehind = newest_selection
2673 .start
2674 .text_anchor
2675 .to_offset(buffer)
2676 .saturating_sub(old_range.start);
2677 let lookahead = old_range
2678 .end
2679 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2680 let mut common_prefix_len = old_text
2681 .bytes()
2682 .zip(text.bytes())
2683 .take_while(|(a, b)| a == b)
2684 .count();
2685
2686 let snapshot = self.buffer.read(cx).snapshot(cx);
2687 let mut ranges = Vec::new();
2688 for selection in &selections {
2689 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2690 let start = selection.start.saturating_sub(lookbehind);
2691 let end = selection.end + lookahead;
2692 ranges.push(start + common_prefix_len..end);
2693 } else {
2694 common_prefix_len = 0;
2695 ranges.clear();
2696 ranges.extend(selections.iter().map(|s| {
2697 if s.id == newest_selection.id {
2698 old_range.clone()
2699 } else {
2700 s.start..s.end
2701 }
2702 }));
2703 break;
2704 }
2705 }
2706 let text = &text[common_prefix_len..];
2707
2708 self.transact(cx, |this, cx| {
2709 if let Some(mut snippet) = snippet {
2710 snippet.text = text.to_string();
2711 for tabstop in snippet.tabstops.iter_mut().flatten() {
2712 tabstop.start -= common_prefix_len as isize;
2713 tabstop.end -= common_prefix_len as isize;
2714 }
2715
2716 this.insert_snippet(&ranges, snippet, cx).log_err();
2717 } else {
2718 this.buffer.update(cx, |buffer, cx| {
2719 buffer.edit(
2720 ranges.iter().map(|range| (range.clone(), text)),
2721 Some(AutoindentMode::EachLine),
2722 cx,
2723 );
2724 });
2725 }
2726
2727 this.refresh_copilot_suggestions(true, cx);
2728 });
2729
2730 let project = self.project.clone()?;
2731 let apply_edits = project.update(cx, |project, cx| {
2732 project.apply_additional_edits_for_completion(
2733 buffer_handle,
2734 completion.clone(),
2735 true,
2736 cx,
2737 )
2738 });
2739 Some(cx.foreground().spawn(async move {
2740 apply_edits.await?;
2741 Ok(())
2742 }))
2743 }
2744
2745 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2746 if matches!(
2747 self.context_menu.as_ref(),
2748 Some(ContextMenu::CodeActions(_))
2749 ) {
2750 self.context_menu.take();
2751 cx.notify();
2752 return;
2753 }
2754
2755 let deployed_from_indicator = action.deployed_from_indicator;
2756 let mut task = self.code_actions_task.take();
2757 cx.spawn(|this, mut cx| async move {
2758 while let Some(prev_task) = task {
2759 prev_task.await;
2760 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
2761 }
2762
2763 this.update(&mut cx, |this, cx| {
2764 if this.focused {
2765 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2766 this.show_context_menu(
2767 ContextMenu::CodeActions(CodeActionsMenu {
2768 buffer,
2769 actions,
2770 selected_item: Default::default(),
2771 list: Default::default(),
2772 deployed_from_indicator,
2773 }),
2774 cx,
2775 );
2776 }
2777 }
2778 })?;
2779
2780 Ok::<_, anyhow::Error>(())
2781 })
2782 .detach_and_log_err(cx);
2783 }
2784
2785 pub fn confirm_code_action(
2786 workspace: &mut Workspace,
2787 action: &ConfirmCodeAction,
2788 cx: &mut ViewContext<Workspace>,
2789 ) -> Option<Task<Result<()>>> {
2790 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2791 let actions_menu = if let ContextMenu::CodeActions(menu) =
2792 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2793 {
2794 menu
2795 } else {
2796 return None;
2797 };
2798 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2799 let action = actions_menu.actions.get(action_ix)?.clone();
2800 let title = action.lsp_action.title.clone();
2801 let buffer = actions_menu.buffer;
2802
2803 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2804 project.apply_code_action(buffer, action, true, cx)
2805 });
2806 let editor = editor.downgrade();
2807 Some(cx.spawn(|workspace, cx| async move {
2808 let project_transaction = apply_code_actions.await?;
2809 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
2810 }))
2811 }
2812
2813 async fn open_project_transaction(
2814 this: &WeakViewHandle<Editor>,
2815 workspace: WeakViewHandle<Workspace>,
2816 transaction: ProjectTransaction,
2817 title: String,
2818 mut cx: AsyncAppContext,
2819 ) -> Result<()> {
2820 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
2821
2822 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2823 entries.sort_unstable_by_key(|(buffer, _)| {
2824 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2825 });
2826
2827 // If the project transaction's edits are all contained within this editor, then
2828 // avoid opening a new editor to display them.
2829
2830 if let Some((buffer, transaction)) = entries.first() {
2831 if entries.len() == 1 {
2832 let excerpt = this.read_with(&cx, |editor, cx| {
2833 editor
2834 .buffer()
2835 .read(cx)
2836 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2837 })?;
2838 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2839 if excerpted_buffer == *buffer {
2840 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
2841 let excerpt_range = excerpt_range.to_offset(buffer);
2842 buffer
2843 .edited_ranges_for_transaction::<usize>(transaction)
2844 .all(|range| {
2845 excerpt_range.start <= range.start
2846 && excerpt_range.end >= range.end
2847 })
2848 });
2849
2850 if all_edits_within_excerpt {
2851 return Ok(());
2852 }
2853 }
2854 }
2855 }
2856 } else {
2857 return Ok(());
2858 }
2859
2860 let mut ranges_to_highlight = Vec::new();
2861 let excerpt_buffer = cx.add_model(|cx| {
2862 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2863 for (buffer_handle, transaction) in &entries {
2864 let buffer = buffer_handle.read(cx);
2865 ranges_to_highlight.extend(
2866 multibuffer.push_excerpts_with_context_lines(
2867 buffer_handle.clone(),
2868 buffer
2869 .edited_ranges_for_transaction::<usize>(transaction)
2870 .collect(),
2871 1,
2872 cx,
2873 ),
2874 );
2875 }
2876 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
2877 multibuffer
2878 });
2879
2880 workspace.update(&mut cx, |workspace, cx| {
2881 let project = workspace.project().clone();
2882 let editor =
2883 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2884 workspace.add_item(Box::new(editor.clone()), cx);
2885 editor.update(cx, |editor, cx| {
2886 editor.highlight_background::<Self>(
2887 ranges_to_highlight,
2888 |theme| theme.editor.highlighted_line_background,
2889 cx,
2890 );
2891 });
2892 })?;
2893
2894 Ok(())
2895 }
2896
2897 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2898 let project = self.project.as_ref()?;
2899 let buffer = self.buffer.read(cx);
2900 let newest_selection = self.selections.newest_anchor().clone();
2901 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2902 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2903 if start_buffer != end_buffer {
2904 return None;
2905 }
2906
2907 let actions = project.update(cx, |project, cx| {
2908 project.code_actions(&start_buffer, start..end, cx)
2909 });
2910 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
2911 let actions = actions.await;
2912 this.update(&mut cx, |this, cx| {
2913 this.available_code_actions = actions.log_err().and_then(|actions| {
2914 if actions.is_empty() {
2915 None
2916 } else {
2917 Some((start_buffer, actions.into()))
2918 }
2919 });
2920 cx.notify();
2921 })
2922 .log_err();
2923 }));
2924 None
2925 }
2926
2927 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2928 if self.pending_rename.is_some() {
2929 return None;
2930 }
2931
2932 let project = self.project.as_ref()?;
2933 let buffer = self.buffer.read(cx);
2934 let newest_selection = self.selections.newest_anchor().clone();
2935 let cursor_position = newest_selection.head();
2936 let (cursor_buffer, cursor_buffer_position) =
2937 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2938 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2939 if cursor_buffer != tail_buffer {
2940 return None;
2941 }
2942
2943 let highlights = project.update(cx, |project, cx| {
2944 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2945 });
2946
2947 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
2948 if let Some(highlights) = highlights.await.log_err() {
2949 this.update(&mut cx, |this, cx| {
2950 if this.pending_rename.is_some() {
2951 return;
2952 }
2953
2954 let buffer_id = cursor_position.buffer_id;
2955 let buffer = this.buffer.read(cx);
2956 if !buffer
2957 .text_anchor_for_position(cursor_position, cx)
2958 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2959 {
2960 return;
2961 }
2962
2963 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2964 let mut write_ranges = Vec::new();
2965 let mut read_ranges = Vec::new();
2966 for highlight in highlights {
2967 for (excerpt_id, excerpt_range) in
2968 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2969 {
2970 let start = highlight
2971 .range
2972 .start
2973 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
2974 let end = highlight
2975 .range
2976 .end
2977 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
2978 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2979 continue;
2980 }
2981
2982 let range = Anchor {
2983 buffer_id,
2984 excerpt_id: excerpt_id.clone(),
2985 text_anchor: start,
2986 }..Anchor {
2987 buffer_id,
2988 excerpt_id,
2989 text_anchor: end,
2990 };
2991 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2992 write_ranges.push(range);
2993 } else {
2994 read_ranges.push(range);
2995 }
2996 }
2997 }
2998
2999 this.highlight_background::<DocumentHighlightRead>(
3000 read_ranges,
3001 |theme| theme.editor.document_highlight_read_background,
3002 cx,
3003 );
3004 this.highlight_background::<DocumentHighlightWrite>(
3005 write_ranges,
3006 |theme| theme.editor.document_highlight_write_background,
3007 cx,
3008 );
3009 cx.notify();
3010 })
3011 .log_err();
3012 }
3013 }));
3014 None
3015 }
3016
3017 fn refresh_copilot_suggestions(
3018 &mut self,
3019 debounce: bool,
3020 cx: &mut ViewContext<Self>,
3021 ) -> Option<()> {
3022 let copilot = Copilot::global(cx)?;
3023 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3024 self.clear_copilot_suggestions(cx);
3025 return None;
3026 }
3027 self.update_visible_copilot_suggestion(cx);
3028
3029 let snapshot = self.buffer.read(cx).snapshot(cx);
3030 let cursor = self.selections.newest_anchor().head();
3031 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3032 self.clear_copilot_suggestions(cx);
3033 return None;
3034 }
3035
3036 let (buffer, buffer_position) =
3037 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3038 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3039 if debounce {
3040 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3041 }
3042
3043 let completions = copilot
3044 .update(&mut cx, |copilot, cx| {
3045 copilot.completions(&buffer, buffer_position, cx)
3046 })
3047 .await
3048 .log_err()
3049 .into_iter()
3050 .flatten()
3051 .collect_vec();
3052
3053 this.update(&mut cx, |this, cx| {
3054 if !completions.is_empty() {
3055 this.copilot_state.cycled = false;
3056 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3057 this.copilot_state.completions.clear();
3058 this.copilot_state.active_completion_index = 0;
3059 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3060 for completion in completions {
3061 this.copilot_state.push_completion(completion);
3062 }
3063 this.update_visible_copilot_suggestion(cx);
3064 }
3065 })
3066 .log_err()?;
3067 Some(())
3068 });
3069
3070 Some(())
3071 }
3072
3073 fn cycle_copilot_suggestions(
3074 &mut self,
3075 direction: Direction,
3076 cx: &mut ViewContext<Self>,
3077 ) -> Option<()> {
3078 let copilot = Copilot::global(cx)?;
3079 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3080 return None;
3081 }
3082
3083 if self.copilot_state.cycled {
3084 self.copilot_state.cycle_completions(direction);
3085 self.update_visible_copilot_suggestion(cx);
3086 } else {
3087 let cursor = self.selections.newest_anchor().head();
3088 let (buffer, buffer_position) =
3089 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3090 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3091 let completions = copilot
3092 .update(&mut cx, |copilot, cx| {
3093 copilot.completions_cycling(&buffer, buffer_position, cx)
3094 })
3095 .await;
3096
3097 this.update(&mut cx, |this, cx| {
3098 this.copilot_state.cycled = true;
3099 for completion in completions.log_err().into_iter().flatten() {
3100 this.copilot_state.push_completion(completion);
3101 }
3102 this.copilot_state.cycle_completions(direction);
3103 this.update_visible_copilot_suggestion(cx);
3104 })
3105 .log_err()?;
3106
3107 Some(())
3108 });
3109 }
3110
3111 Some(())
3112 }
3113
3114 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3115 if !self.has_active_copilot_suggestion(cx) {
3116 self.refresh_copilot_suggestions(false, cx);
3117 return;
3118 }
3119
3120 self.update_visible_copilot_suggestion(cx);
3121 }
3122
3123 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3124 if self.has_active_copilot_suggestion(cx) {
3125 self.cycle_copilot_suggestions(Direction::Next, cx);
3126 } else {
3127 self.refresh_copilot_suggestions(false, cx);
3128 }
3129 }
3130
3131 fn previous_copilot_suggestion(
3132 &mut self,
3133 _: &copilot::PreviousSuggestion,
3134 cx: &mut ViewContext<Self>,
3135 ) {
3136 if self.has_active_copilot_suggestion(cx) {
3137 self.cycle_copilot_suggestions(Direction::Prev, cx);
3138 } else {
3139 self.refresh_copilot_suggestions(false, cx);
3140 }
3141 }
3142
3143 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3144 if let Some(suggestion) = self
3145 .display_map
3146 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx))
3147 {
3148 if let Some((copilot, completion)) =
3149 Copilot::global(cx).zip(self.copilot_state.active_completion())
3150 {
3151 copilot
3152 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3153 .detach_and_log_err(cx);
3154
3155 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3156 }
3157 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3158 cx.notify();
3159 true
3160 } else {
3161 false
3162 }
3163 }
3164
3165 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3166 if self.has_active_copilot_suggestion(cx) {
3167 if let Some(copilot) = Copilot::global(cx) {
3168 copilot
3169 .update(cx, |copilot, cx| {
3170 copilot.discard_completions(&self.copilot_state.completions, cx)
3171 })
3172 .detach_and_log_err(cx);
3173
3174 self.report_copilot_event(None, false, cx)
3175 }
3176
3177 self.display_map
3178 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
3179 cx.notify();
3180 true
3181 } else {
3182 false
3183 }
3184 }
3185
3186 fn is_copilot_enabled_at(
3187 &self,
3188 location: Anchor,
3189 snapshot: &MultiBufferSnapshot,
3190 cx: &mut ViewContext<Self>,
3191 ) -> bool {
3192 let path = snapshot.file_at(location).map(|file| file.path().as_ref());
3193 let language_name = snapshot
3194 .language_at(location)
3195 .map(|language| language.name());
3196 let settings = all_language_settings(cx);
3197 settings.copilot_enabled(language_name.as_deref(), path)
3198 }
3199
3200 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3201 self.display_map.read(cx).has_suggestion()
3202 }
3203
3204 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3205 let snapshot = self.buffer.read(cx).snapshot(cx);
3206 let selection = self.selections.newest_anchor();
3207 let cursor = selection.head();
3208
3209 if self.context_menu.is_some()
3210 || !self.completion_tasks.is_empty()
3211 || selection.start != selection.end
3212 {
3213 self.discard_copilot_suggestion(cx);
3214 } else if let Some(text) = self
3215 .copilot_state
3216 .text_for_active_completion(cursor, &snapshot)
3217 {
3218 self.display_map.update(cx, move |map, cx| {
3219 map.replace_suggestion(
3220 Some(Suggestion {
3221 position: cursor,
3222 text: text.trim_end().into(),
3223 }),
3224 cx,
3225 )
3226 });
3227 cx.notify();
3228 } else {
3229 self.discard_copilot_suggestion(cx);
3230 }
3231 }
3232
3233 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3234 self.copilot_state = Default::default();
3235 self.discard_copilot_suggestion(cx);
3236 }
3237
3238 pub fn render_code_actions_indicator(
3239 &self,
3240 style: &EditorStyle,
3241 active: bool,
3242 cx: &mut ViewContext<Self>,
3243 ) -> Option<AnyElement<Self>> {
3244 if self.available_code_actions.is_some() {
3245 enum CodeActions {}
3246 Some(
3247 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3248 Svg::new("icons/bolt_8.svg")
3249 .with_color(style.code_actions.indicator.style_for(state, active).color)
3250 })
3251 .with_cursor_style(CursorStyle::PointingHand)
3252 .with_padding(Padding::uniform(3.))
3253 .on_down(MouseButton::Left, |_, this, cx| {
3254 this.toggle_code_actions(
3255 &ToggleCodeActions {
3256 deployed_from_indicator: true,
3257 },
3258 cx,
3259 );
3260 })
3261 .into_any(),
3262 )
3263 } else {
3264 None
3265 }
3266 }
3267
3268 pub fn render_fold_indicators(
3269 &self,
3270 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3271 style: &EditorStyle,
3272 gutter_hovered: bool,
3273 line_height: f32,
3274 gutter_margin: f32,
3275 cx: &mut ViewContext<Self>,
3276 ) -> Vec<Option<AnyElement<Self>>> {
3277 enum FoldIndicators {}
3278
3279 let style = style.folds.clone();
3280
3281 fold_data
3282 .iter()
3283 .enumerate()
3284 .map(|(ix, fold_data)| {
3285 fold_data
3286 .map(|(fold_status, buffer_row, active)| {
3287 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3288 MouseEventHandler::<FoldIndicators, _>::new(
3289 ix as usize,
3290 cx,
3291 |mouse_state, _| {
3292 Svg::new(match fold_status {
3293 FoldStatus::Folded => style.folded_icon.clone(),
3294 FoldStatus::Foldable => style.foldable_icon.clone(),
3295 })
3296 .with_color(
3297 style
3298 .indicator
3299 .style_for(
3300 mouse_state,
3301 fold_status == FoldStatus::Folded,
3302 )
3303 .color,
3304 )
3305 .constrained()
3306 .with_width(gutter_margin * style.icon_margin_scale)
3307 .aligned()
3308 .constrained()
3309 .with_height(line_height)
3310 .with_width(gutter_margin)
3311 .aligned()
3312 },
3313 )
3314 .with_cursor_style(CursorStyle::PointingHand)
3315 .with_padding(Padding::uniform(3.))
3316 .on_click(MouseButton::Left, {
3317 move |_, editor, cx| match fold_status {
3318 FoldStatus::Folded => {
3319 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3320 }
3321 FoldStatus::Foldable => {
3322 editor.fold_at(&FoldAt { buffer_row }, cx);
3323 }
3324 }
3325 })
3326 .into_any()
3327 })
3328 })
3329 .flatten()
3330 })
3331 .collect()
3332 }
3333
3334 pub fn context_menu_visible(&self) -> bool {
3335 self.context_menu
3336 .as_ref()
3337 .map_or(false, |menu| menu.visible())
3338 }
3339
3340 pub fn render_context_menu(
3341 &self,
3342 cursor_position: DisplayPoint,
3343 style: EditorStyle,
3344 cx: &mut ViewContext<Editor>,
3345 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3346 self.context_menu
3347 .as_ref()
3348 .map(|menu| menu.render(cursor_position, style, cx))
3349 }
3350
3351 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3352 if !matches!(menu, ContextMenu::Completions(_)) {
3353 self.completion_tasks.clear();
3354 }
3355 self.context_menu = Some(menu);
3356 self.discard_copilot_suggestion(cx);
3357 cx.notify();
3358 }
3359
3360 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3361 cx.notify();
3362 self.completion_tasks.clear();
3363 let context_menu = self.context_menu.take();
3364 if context_menu.is_some() {
3365 self.update_visible_copilot_suggestion(cx);
3366 }
3367 context_menu
3368 }
3369
3370 pub fn insert_snippet(
3371 &mut self,
3372 insertion_ranges: &[Range<usize>],
3373 snippet: Snippet,
3374 cx: &mut ViewContext<Self>,
3375 ) -> Result<()> {
3376 let tabstops = self.buffer.update(cx, |buffer, cx| {
3377 let snippet_text: Arc<str> = snippet.text.clone().into();
3378 buffer.edit(
3379 insertion_ranges
3380 .iter()
3381 .cloned()
3382 .map(|range| (range, snippet_text.clone())),
3383 Some(AutoindentMode::EachLine),
3384 cx,
3385 );
3386
3387 let snapshot = &*buffer.read(cx);
3388 let snippet = &snippet;
3389 snippet
3390 .tabstops
3391 .iter()
3392 .map(|tabstop| {
3393 let mut tabstop_ranges = tabstop
3394 .iter()
3395 .flat_map(|tabstop_range| {
3396 let mut delta = 0_isize;
3397 insertion_ranges.iter().map(move |insertion_range| {
3398 let insertion_start = insertion_range.start as isize + delta;
3399 delta +=
3400 snippet.text.len() as isize - insertion_range.len() as isize;
3401
3402 let start = snapshot.anchor_before(
3403 (insertion_start + tabstop_range.start) as usize,
3404 );
3405 let end = snapshot
3406 .anchor_after((insertion_start + tabstop_range.end) as usize);
3407 start..end
3408 })
3409 })
3410 .collect::<Vec<_>>();
3411 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3412 tabstop_ranges
3413 })
3414 .collect::<Vec<_>>()
3415 });
3416
3417 if let Some(tabstop) = tabstops.first() {
3418 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3419 s.select_ranges(tabstop.iter().cloned());
3420 });
3421 self.snippet_stack.push(SnippetState {
3422 active_index: 0,
3423 ranges: tabstops,
3424 });
3425 }
3426
3427 Ok(())
3428 }
3429
3430 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3431 self.move_to_snippet_tabstop(Bias::Right, cx)
3432 }
3433
3434 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3435 self.move_to_snippet_tabstop(Bias::Left, cx)
3436 }
3437
3438 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3439 if let Some(mut snippet) = self.snippet_stack.pop() {
3440 match bias {
3441 Bias::Left => {
3442 if snippet.active_index > 0 {
3443 snippet.active_index -= 1;
3444 } else {
3445 self.snippet_stack.push(snippet);
3446 return false;
3447 }
3448 }
3449 Bias::Right => {
3450 if snippet.active_index + 1 < snippet.ranges.len() {
3451 snippet.active_index += 1;
3452 } else {
3453 self.snippet_stack.push(snippet);
3454 return false;
3455 }
3456 }
3457 }
3458 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3459 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3460 s.select_anchor_ranges(current_ranges.iter().cloned())
3461 });
3462 // If snippet state is not at the last tabstop, push it back on the stack
3463 if snippet.active_index + 1 < snippet.ranges.len() {
3464 self.snippet_stack.push(snippet);
3465 }
3466 return true;
3467 }
3468 }
3469
3470 false
3471 }
3472
3473 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3474 self.transact(cx, |this, cx| {
3475 this.select_all(&SelectAll, cx);
3476 this.insert("", cx);
3477 });
3478 }
3479
3480 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3481 self.transact(cx, |this, cx| {
3482 this.select_autoclose_pair(cx);
3483 let mut selections = this.selections.all::<Point>(cx);
3484 if !this.selections.line_mode {
3485 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3486 for selection in &mut selections {
3487 if selection.is_empty() {
3488 let old_head = selection.head();
3489 let mut new_head =
3490 movement::left(&display_map, old_head.to_display_point(&display_map))
3491 .to_point(&display_map);
3492 if let Some((buffer, line_buffer_range)) = display_map
3493 .buffer_snapshot
3494 .buffer_line_for_row(old_head.row)
3495 {
3496 let indent_size =
3497 buffer.indent_size_for_line(line_buffer_range.start.row);
3498 let indent_len = match indent_size.kind {
3499 IndentKind::Space => {
3500 buffer.settings_at(line_buffer_range.start, cx).tab_size
3501 }
3502 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3503 };
3504 if old_head.column <= indent_size.len && old_head.column > 0 {
3505 let indent_len = indent_len.get();
3506 new_head = cmp::min(
3507 new_head,
3508 Point::new(
3509 old_head.row,
3510 ((old_head.column - 1) / indent_len) * indent_len,
3511 ),
3512 );
3513 }
3514 }
3515
3516 selection.set_head(new_head, SelectionGoal::None);
3517 }
3518 }
3519 }
3520
3521 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3522 this.insert("", cx);
3523 this.refresh_copilot_suggestions(true, cx);
3524 });
3525 }
3526
3527 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3528 self.transact(cx, |this, cx| {
3529 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3530 let line_mode = s.line_mode;
3531 s.move_with(|map, selection| {
3532 if selection.is_empty() && !line_mode {
3533 let cursor = movement::right(map, selection.head());
3534 selection.set_head(cursor, SelectionGoal::None);
3535 }
3536 })
3537 });
3538 this.insert("", cx);
3539 this.refresh_copilot_suggestions(true, cx);
3540 });
3541 }
3542
3543 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3544 if self.move_to_prev_snippet_tabstop(cx) {
3545 return;
3546 }
3547
3548 self.outdent(&Outdent, cx);
3549 }
3550
3551 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3552 if self.move_to_next_snippet_tabstop(cx) {
3553 return;
3554 }
3555
3556 let mut selections = self.selections.all_adjusted(cx);
3557 let buffer = self.buffer.read(cx);
3558 let snapshot = buffer.snapshot(cx);
3559 let rows_iter = selections.iter().map(|s| s.head().row);
3560 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3561
3562 let mut edits = Vec::new();
3563 let mut prev_edited_row = 0;
3564 let mut row_delta = 0;
3565 for selection in &mut selections {
3566 if selection.start.row != prev_edited_row {
3567 row_delta = 0;
3568 }
3569 prev_edited_row = selection.end.row;
3570
3571 // If the selection is non-empty, then increase the indentation of the selected lines.
3572 if !selection.is_empty() {
3573 row_delta =
3574 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3575 continue;
3576 }
3577
3578 // If the selection is empty and the cursor is in the leading whitespace before the
3579 // suggested indentation, then auto-indent the line.
3580 let cursor = selection.head();
3581 let current_indent = snapshot.indent_size_for_line(cursor.row);
3582 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3583 if cursor.column < suggested_indent.len
3584 && cursor.column <= current_indent.len
3585 && current_indent.len <= suggested_indent.len
3586 {
3587 selection.start = Point::new(cursor.row, suggested_indent.len);
3588 selection.end = selection.start;
3589 if row_delta == 0 {
3590 edits.extend(Buffer::edit_for_indent_size_adjustment(
3591 cursor.row,
3592 current_indent,
3593 suggested_indent,
3594 ));
3595 row_delta = suggested_indent.len - current_indent.len;
3596 }
3597 continue;
3598 }
3599 }
3600
3601 // Accept copilot suggestion if there is only one selection and the cursor is not
3602 // in the leading whitespace.
3603 if self.selections.count() == 1
3604 && cursor.column >= current_indent.len
3605 && self.has_active_copilot_suggestion(cx)
3606 {
3607 self.accept_copilot_suggestion(cx);
3608 return;
3609 }
3610
3611 // Otherwise, insert a hard or soft tab.
3612 let settings = buffer.settings_at(cursor, cx);
3613 let tab_size = if settings.hard_tabs {
3614 IndentSize::tab()
3615 } else {
3616 let tab_size = settings.tab_size.get();
3617 let char_column = snapshot
3618 .text_for_range(Point::new(cursor.row, 0)..cursor)
3619 .flat_map(str::chars)
3620 .count()
3621 + row_delta as usize;
3622 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3623 IndentSize::spaces(chars_to_next_tab_stop)
3624 };
3625 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3626 selection.end = selection.start;
3627 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3628 row_delta += tab_size.len;
3629 }
3630
3631 self.transact(cx, |this, cx| {
3632 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3633 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3634 this.refresh_copilot_suggestions(true, cx);
3635 });
3636 }
3637
3638 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3639 let mut selections = self.selections.all::<Point>(cx);
3640 let mut prev_edited_row = 0;
3641 let mut row_delta = 0;
3642 let mut edits = Vec::new();
3643 let buffer = self.buffer.read(cx);
3644 let snapshot = buffer.snapshot(cx);
3645 for selection in &mut selections {
3646 if selection.start.row != prev_edited_row {
3647 row_delta = 0;
3648 }
3649 prev_edited_row = selection.end.row;
3650
3651 row_delta =
3652 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3653 }
3654
3655 self.transact(cx, |this, cx| {
3656 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3657 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3658 });
3659 }
3660
3661 fn indent_selection(
3662 buffer: &MultiBuffer,
3663 snapshot: &MultiBufferSnapshot,
3664 selection: &mut Selection<Point>,
3665 edits: &mut Vec<(Range<Point>, String)>,
3666 delta_for_start_row: u32,
3667 cx: &AppContext,
3668 ) -> u32 {
3669 let settings = buffer.settings_at(selection.start, cx);
3670 let tab_size = settings.tab_size.get();
3671 let indent_kind = if settings.hard_tabs {
3672 IndentKind::Tab
3673 } else {
3674 IndentKind::Space
3675 };
3676 let mut start_row = selection.start.row;
3677 let mut end_row = selection.end.row + 1;
3678
3679 // If a selection ends at the beginning of a line, don't indent
3680 // that last line.
3681 if selection.end.column == 0 {
3682 end_row -= 1;
3683 }
3684
3685 // Avoid re-indenting a row that has already been indented by a
3686 // previous selection, but still update this selection's column
3687 // to reflect that indentation.
3688 if delta_for_start_row > 0 {
3689 start_row += 1;
3690 selection.start.column += delta_for_start_row;
3691 if selection.end.row == selection.start.row {
3692 selection.end.column += delta_for_start_row;
3693 }
3694 }
3695
3696 let mut delta_for_end_row = 0;
3697 for row in start_row..end_row {
3698 let current_indent = snapshot.indent_size_for_line(row);
3699 let indent_delta = match (current_indent.kind, indent_kind) {
3700 (IndentKind::Space, IndentKind::Space) => {
3701 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3702 IndentSize::spaces(columns_to_next_tab_stop)
3703 }
3704 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3705 (_, IndentKind::Tab) => IndentSize::tab(),
3706 };
3707
3708 let row_start = Point::new(row, 0);
3709 edits.push((
3710 row_start..row_start,
3711 indent_delta.chars().collect::<String>(),
3712 ));
3713
3714 // Update this selection's endpoints to reflect the indentation.
3715 if row == selection.start.row {
3716 selection.start.column += indent_delta.len;
3717 }
3718 if row == selection.end.row {
3719 selection.end.column += indent_delta.len;
3720 delta_for_end_row = indent_delta.len;
3721 }
3722 }
3723
3724 if selection.start.row == selection.end.row {
3725 delta_for_start_row + delta_for_end_row
3726 } else {
3727 delta_for_end_row
3728 }
3729 }
3730
3731 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3732 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3733 let selections = self.selections.all::<Point>(cx);
3734 let mut deletion_ranges = Vec::new();
3735 let mut last_outdent = None;
3736 {
3737 let buffer = self.buffer.read(cx);
3738 let snapshot = buffer.snapshot(cx);
3739 for selection in &selections {
3740 let settings = buffer.settings_at(selection.start, cx);
3741 let tab_size = settings.tab_size.get();
3742 let mut rows = selection.spanned_rows(false, &display_map);
3743
3744 // Avoid re-outdenting a row that has already been outdented by a
3745 // previous selection.
3746 if let Some(last_row) = last_outdent {
3747 if last_row == rows.start {
3748 rows.start += 1;
3749 }
3750 }
3751
3752 for row in rows {
3753 let indent_size = snapshot.indent_size_for_line(row);
3754 if indent_size.len > 0 {
3755 let deletion_len = match indent_size.kind {
3756 IndentKind::Space => {
3757 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3758 if columns_to_prev_tab_stop == 0 {
3759 tab_size
3760 } else {
3761 columns_to_prev_tab_stop
3762 }
3763 }
3764 IndentKind::Tab => 1,
3765 };
3766 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3767 last_outdent = Some(row);
3768 }
3769 }
3770 }
3771 }
3772
3773 self.transact(cx, |this, cx| {
3774 this.buffer.update(cx, |buffer, cx| {
3775 let empty_str: Arc<str> = "".into();
3776 buffer.edit(
3777 deletion_ranges
3778 .into_iter()
3779 .map(|range| (range, empty_str.clone())),
3780 None,
3781 cx,
3782 );
3783 });
3784 let selections = this.selections.all::<usize>(cx);
3785 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3786 });
3787 }
3788
3789 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3790 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3791 let selections = self.selections.all::<Point>(cx);
3792
3793 let mut new_cursors = Vec::new();
3794 let mut edit_ranges = Vec::new();
3795 let mut selections = selections.iter().peekable();
3796 while let Some(selection) = selections.next() {
3797 let mut rows = selection.spanned_rows(false, &display_map);
3798 let goal_display_column = selection.head().to_display_point(&display_map).column();
3799
3800 // Accumulate contiguous regions of rows that we want to delete.
3801 while let Some(next_selection) = selections.peek() {
3802 let next_rows = next_selection.spanned_rows(false, &display_map);
3803 if next_rows.start <= rows.end {
3804 rows.end = next_rows.end;
3805 selections.next().unwrap();
3806 } else {
3807 break;
3808 }
3809 }
3810
3811 let buffer = &display_map.buffer_snapshot;
3812 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3813 let edit_end;
3814 let cursor_buffer_row;
3815 if buffer.max_point().row >= rows.end {
3816 // If there's a line after the range, delete the \n from the end of the row range
3817 // and position the cursor on the next line.
3818 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3819 cursor_buffer_row = rows.end;
3820 } else {
3821 // If there isn't a line after the range, delete the \n from the line before the
3822 // start of the row range and position the cursor there.
3823 edit_start = edit_start.saturating_sub(1);
3824 edit_end = buffer.len();
3825 cursor_buffer_row = rows.start.saturating_sub(1);
3826 }
3827
3828 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3829 *cursor.column_mut() =
3830 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3831
3832 new_cursors.push((
3833 selection.id,
3834 buffer.anchor_after(cursor.to_point(&display_map)),
3835 ));
3836 edit_ranges.push(edit_start..edit_end);
3837 }
3838
3839 self.transact(cx, |this, cx| {
3840 let buffer = this.buffer.update(cx, |buffer, cx| {
3841 let empty_str: Arc<str> = "".into();
3842 buffer.edit(
3843 edit_ranges
3844 .into_iter()
3845 .map(|range| (range, empty_str.clone())),
3846 None,
3847 cx,
3848 );
3849 buffer.snapshot(cx)
3850 });
3851 let new_selections = new_cursors
3852 .into_iter()
3853 .map(|(id, cursor)| {
3854 let cursor = cursor.to_point(&buffer);
3855 Selection {
3856 id,
3857 start: cursor,
3858 end: cursor,
3859 reversed: false,
3860 goal: SelectionGoal::None,
3861 }
3862 })
3863 .collect();
3864
3865 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3866 s.select(new_selections);
3867 });
3868 });
3869 }
3870
3871 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3872 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3873 let buffer = &display_map.buffer_snapshot;
3874 let selections = self.selections.all::<Point>(cx);
3875
3876 let mut edits = Vec::new();
3877 let mut selections_iter = selections.iter().peekable();
3878 while let Some(selection) = selections_iter.next() {
3879 // Avoid duplicating the same lines twice.
3880 let mut rows = selection.spanned_rows(false, &display_map);
3881
3882 while let Some(next_selection) = selections_iter.peek() {
3883 let next_rows = next_selection.spanned_rows(false, &display_map);
3884 if next_rows.start < rows.end {
3885 rows.end = next_rows.end;
3886 selections_iter.next().unwrap();
3887 } else {
3888 break;
3889 }
3890 }
3891
3892 // Copy the text from the selected row region and splice it at the start of the region.
3893 let start = Point::new(rows.start, 0);
3894 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3895 let text = buffer
3896 .text_for_range(start..end)
3897 .chain(Some("\n"))
3898 .collect::<String>();
3899 edits.push((start..start, text));
3900 }
3901
3902 self.transact(cx, |this, cx| {
3903 this.buffer.update(cx, |buffer, cx| {
3904 buffer.edit(edits, None, cx);
3905 });
3906
3907 this.request_autoscroll(Autoscroll::fit(), cx);
3908 });
3909 }
3910
3911 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3912 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3913 let buffer = self.buffer.read(cx).snapshot(cx);
3914
3915 let mut edits = Vec::new();
3916 let mut unfold_ranges = Vec::new();
3917 let mut refold_ranges = Vec::new();
3918
3919 let selections = self.selections.all::<Point>(cx);
3920 let mut selections = selections.iter().peekable();
3921 let mut contiguous_row_selections = Vec::new();
3922 let mut new_selections = Vec::new();
3923
3924 while let Some(selection) = selections.next() {
3925 // Find all the selections that span a contiguous row range
3926 let (start_row, end_row) = consume_contiguous_rows(
3927 &mut contiguous_row_selections,
3928 selection,
3929 &display_map,
3930 &mut selections,
3931 );
3932
3933 // Move the text spanned by the row range to be before the line preceding the row range
3934 if start_row > 0 {
3935 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3936 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3937 let insertion_point = display_map
3938 .prev_line_boundary(Point::new(start_row - 1, 0))
3939 .0;
3940
3941 // Don't move lines across excerpts
3942 if buffer
3943 .excerpt_boundaries_in_range((
3944 Bound::Excluded(insertion_point),
3945 Bound::Included(range_to_move.end),
3946 ))
3947 .next()
3948 .is_none()
3949 {
3950 let text = buffer
3951 .text_for_range(range_to_move.clone())
3952 .flat_map(|s| s.chars())
3953 .skip(1)
3954 .chain(['\n'])
3955 .collect::<String>();
3956
3957 edits.push((
3958 buffer.anchor_after(range_to_move.start)
3959 ..buffer.anchor_before(range_to_move.end),
3960 String::new(),
3961 ));
3962 let insertion_anchor = buffer.anchor_after(insertion_point);
3963 edits.push((insertion_anchor..insertion_anchor, text));
3964
3965 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3966
3967 // Move selections up
3968 new_selections.extend(contiguous_row_selections.drain(..).map(
3969 |mut selection| {
3970 selection.start.row -= row_delta;
3971 selection.end.row -= row_delta;
3972 selection
3973 },
3974 ));
3975
3976 // Move folds up
3977 unfold_ranges.push(range_to_move.clone());
3978 for fold in display_map.folds_in_range(
3979 buffer.anchor_before(range_to_move.start)
3980 ..buffer.anchor_after(range_to_move.end),
3981 ) {
3982 let mut start = fold.start.to_point(&buffer);
3983 let mut end = fold.end.to_point(&buffer);
3984 start.row -= row_delta;
3985 end.row -= row_delta;
3986 refold_ranges.push(start..end);
3987 }
3988 }
3989 }
3990
3991 // If we didn't move line(s), preserve the existing selections
3992 new_selections.append(&mut contiguous_row_selections);
3993 }
3994
3995 self.transact(cx, |this, cx| {
3996 this.unfold_ranges(unfold_ranges, true, true, cx);
3997 this.buffer.update(cx, |buffer, cx| {
3998 for (range, text) in edits {
3999 buffer.edit([(range, text)], None, cx);
4000 }
4001 });
4002 this.fold_ranges(refold_ranges, true, cx);
4003 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4004 s.select(new_selections);
4005 })
4006 });
4007 }
4008
4009 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4010 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4011 let buffer = self.buffer.read(cx).snapshot(cx);
4012
4013 let mut edits = Vec::new();
4014 let mut unfold_ranges = Vec::new();
4015 let mut refold_ranges = Vec::new();
4016
4017 let selections = self.selections.all::<Point>(cx);
4018 let mut selections = selections.iter().peekable();
4019 let mut contiguous_row_selections = Vec::new();
4020 let mut new_selections = Vec::new();
4021
4022 while let Some(selection) = selections.next() {
4023 // Find all the selections that span a contiguous row range
4024 let (start_row, end_row) = consume_contiguous_rows(
4025 &mut contiguous_row_selections,
4026 selection,
4027 &display_map,
4028 &mut selections,
4029 );
4030
4031 // Move the text spanned by the row range to be after the last line of the row range
4032 if end_row <= buffer.max_point().row {
4033 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4034 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4035
4036 // Don't move lines across excerpt boundaries
4037 if buffer
4038 .excerpt_boundaries_in_range((
4039 Bound::Excluded(range_to_move.start),
4040 Bound::Included(insertion_point),
4041 ))
4042 .next()
4043 .is_none()
4044 {
4045 let mut text = String::from("\n");
4046 text.extend(buffer.text_for_range(range_to_move.clone()));
4047 text.pop(); // Drop trailing newline
4048 edits.push((
4049 buffer.anchor_after(range_to_move.start)
4050 ..buffer.anchor_before(range_to_move.end),
4051 String::new(),
4052 ));
4053 let insertion_anchor = buffer.anchor_after(insertion_point);
4054 edits.push((insertion_anchor..insertion_anchor, text));
4055
4056 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4057
4058 // Move selections down
4059 new_selections.extend(contiguous_row_selections.drain(..).map(
4060 |mut selection| {
4061 selection.start.row += row_delta;
4062 selection.end.row += row_delta;
4063 selection
4064 },
4065 ));
4066
4067 // Move folds down
4068 unfold_ranges.push(range_to_move.clone());
4069 for fold in display_map.folds_in_range(
4070 buffer.anchor_before(range_to_move.start)
4071 ..buffer.anchor_after(range_to_move.end),
4072 ) {
4073 let mut start = fold.start.to_point(&buffer);
4074 let mut end = fold.end.to_point(&buffer);
4075 start.row += row_delta;
4076 end.row += row_delta;
4077 refold_ranges.push(start..end);
4078 }
4079 }
4080 }
4081
4082 // If we didn't move line(s), preserve the existing selections
4083 new_selections.append(&mut contiguous_row_selections);
4084 }
4085
4086 self.transact(cx, |this, cx| {
4087 this.unfold_ranges(unfold_ranges, true, true, cx);
4088 this.buffer.update(cx, |buffer, cx| {
4089 for (range, text) in edits {
4090 buffer.edit([(range, text)], None, cx);
4091 }
4092 });
4093 this.fold_ranges(refold_ranges, true, cx);
4094 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4095 });
4096 }
4097
4098 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4099 self.transact(cx, |this, cx| {
4100 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4101 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4102 let line_mode = s.line_mode;
4103 s.move_with(|display_map, selection| {
4104 if !selection.is_empty() || line_mode {
4105 return;
4106 }
4107
4108 let mut head = selection.head();
4109 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4110 if head.column() == display_map.line_len(head.row()) {
4111 transpose_offset = display_map
4112 .buffer_snapshot
4113 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4114 }
4115
4116 if transpose_offset == 0 {
4117 return;
4118 }
4119
4120 *head.column_mut() += 1;
4121 head = display_map.clip_point(head, Bias::Right);
4122 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4123
4124 let transpose_start = display_map
4125 .buffer_snapshot
4126 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4127 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4128 let transpose_end = display_map
4129 .buffer_snapshot
4130 .clip_offset(transpose_offset + 1, Bias::Right);
4131 if let Some(ch) =
4132 display_map.buffer_snapshot.chars_at(transpose_start).next()
4133 {
4134 edits.push((transpose_start..transpose_offset, String::new()));
4135 edits.push((transpose_end..transpose_end, ch.to_string()));
4136 }
4137 }
4138 });
4139 edits
4140 });
4141 this.buffer
4142 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4143 let selections = this.selections.all::<usize>(cx);
4144 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4145 s.select(selections);
4146 });
4147 });
4148 }
4149
4150 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4151 let mut text = String::new();
4152 let buffer = self.buffer.read(cx).snapshot(cx);
4153 let mut selections = self.selections.all::<Point>(cx);
4154 let mut clipboard_selections = Vec::with_capacity(selections.len());
4155 {
4156 let max_point = buffer.max_point();
4157 for selection in &mut selections {
4158 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4159 if is_entire_line {
4160 selection.start = Point::new(selection.start.row, 0);
4161 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4162 selection.goal = SelectionGoal::None;
4163 }
4164 let mut len = 0;
4165 for chunk in buffer.text_for_range(selection.start..selection.end) {
4166 text.push_str(chunk);
4167 len += chunk.len();
4168 }
4169 clipboard_selections.push(ClipboardSelection {
4170 len,
4171 is_entire_line,
4172 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4173 });
4174 }
4175 }
4176
4177 self.transact(cx, |this, cx| {
4178 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4179 s.select(selections);
4180 });
4181 this.insert("", cx);
4182 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4183 });
4184 }
4185
4186 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4187 let selections = self.selections.all::<Point>(cx);
4188 let buffer = self.buffer.read(cx).read(cx);
4189 let mut text = String::new();
4190
4191 let mut clipboard_selections = Vec::with_capacity(selections.len());
4192 {
4193 let max_point = buffer.max_point();
4194 for selection in selections.iter() {
4195 let mut start = selection.start;
4196 let mut end = selection.end;
4197 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4198 if is_entire_line {
4199 start = Point::new(start.row, 0);
4200 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4201 }
4202 let mut len = 0;
4203 for chunk in buffer.text_for_range(start..end) {
4204 text.push_str(chunk);
4205 len += chunk.len();
4206 }
4207 clipboard_selections.push(ClipboardSelection {
4208 len,
4209 is_entire_line,
4210 first_line_indent: buffer.indent_size_for_line(start.row).len,
4211 });
4212 }
4213 }
4214
4215 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4216 }
4217
4218 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4219 self.transact(cx, |this, cx| {
4220 if let Some(item) = cx.read_from_clipboard() {
4221 let mut clipboard_text = Cow::Borrowed(item.text());
4222 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4223 let old_selections = this.selections.all::<usize>(cx);
4224 let all_selections_were_entire_line =
4225 clipboard_selections.iter().all(|s| s.is_entire_line);
4226 let first_selection_indent_column =
4227 clipboard_selections.first().map(|s| s.first_line_indent);
4228 if clipboard_selections.len() != old_selections.len() {
4229 let mut newline_separated_text = String::new();
4230 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4231 let mut ix = 0;
4232 while let Some(clipboard_selection) = clipboard_selections.next() {
4233 newline_separated_text
4234 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4235 ix += clipboard_selection.len;
4236 if clipboard_selections.peek().is_some() {
4237 newline_separated_text.push('\n');
4238 }
4239 }
4240 clipboard_text = Cow::Owned(newline_separated_text);
4241 }
4242
4243 this.buffer.update(cx, |buffer, cx| {
4244 let snapshot = buffer.read(cx);
4245 let mut start_offset = 0;
4246 let mut edits = Vec::new();
4247 let mut original_indent_columns = Vec::new();
4248 let line_mode = this.selections.line_mode;
4249 for (ix, selection) in old_selections.iter().enumerate() {
4250 let to_insert;
4251 let entire_line;
4252 let original_indent_column;
4253 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4254 let end_offset = start_offset + clipboard_selection.len;
4255 to_insert = &clipboard_text[start_offset..end_offset];
4256 entire_line = clipboard_selection.is_entire_line;
4257 start_offset = end_offset;
4258 original_indent_column =
4259 Some(clipboard_selection.first_line_indent);
4260 } else {
4261 to_insert = clipboard_text.as_str();
4262 entire_line = all_selections_were_entire_line;
4263 original_indent_column = first_selection_indent_column
4264 }
4265
4266 // If the corresponding selection was empty when this slice of the
4267 // clipboard text was written, then the entire line containing the
4268 // selection was copied. If this selection is also currently empty,
4269 // then paste the line before the current line of the buffer.
4270 let range = if selection.is_empty() && !line_mode && entire_line {
4271 let column = selection.start.to_point(&snapshot).column as usize;
4272 let line_start = selection.start - column;
4273 line_start..line_start
4274 } else {
4275 selection.range()
4276 };
4277
4278 edits.push((range, to_insert));
4279 original_indent_columns.extend(original_indent_column);
4280 }
4281 drop(snapshot);
4282
4283 buffer.edit(
4284 edits,
4285 Some(AutoindentMode::Block {
4286 original_indent_columns,
4287 }),
4288 cx,
4289 );
4290 });
4291
4292 let selections = this.selections.all::<usize>(cx);
4293 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4294 } else {
4295 this.insert(&clipboard_text, cx);
4296 }
4297 }
4298 });
4299 }
4300
4301 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4302 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4303 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4304 self.change_selections(None, cx, |s| {
4305 s.select_anchors(selections.to_vec());
4306 });
4307 }
4308 self.request_autoscroll(Autoscroll::fit(), cx);
4309 self.unmark_text(cx);
4310 self.refresh_copilot_suggestions(true, cx);
4311 cx.emit(Event::Edited);
4312 }
4313 }
4314
4315 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4316 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4317 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4318 {
4319 self.change_selections(None, cx, |s| {
4320 s.select_anchors(selections.to_vec());
4321 });
4322 }
4323 self.request_autoscroll(Autoscroll::fit(), cx);
4324 self.unmark_text(cx);
4325 self.refresh_copilot_suggestions(true, cx);
4326 cx.emit(Event::Edited);
4327 }
4328 }
4329
4330 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4331 self.buffer
4332 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4333 }
4334
4335 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4336 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4337 let line_mode = s.line_mode;
4338 s.move_with(|map, selection| {
4339 let cursor = if selection.is_empty() && !line_mode {
4340 movement::left(map, selection.start)
4341 } else {
4342 selection.start
4343 };
4344 selection.collapse_to(cursor, SelectionGoal::None);
4345 });
4346 })
4347 }
4348
4349 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4350 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4351 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4352 })
4353 }
4354
4355 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4356 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4357 let line_mode = s.line_mode;
4358 s.move_with(|map, selection| {
4359 let cursor = if selection.is_empty() && !line_mode {
4360 movement::right(map, selection.end)
4361 } else {
4362 selection.end
4363 };
4364 selection.collapse_to(cursor, SelectionGoal::None)
4365 });
4366 })
4367 }
4368
4369 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4370 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4371 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4372 })
4373 }
4374
4375 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4376 if self.take_rename(true, cx).is_some() {
4377 return;
4378 }
4379
4380 if let Some(context_menu) = self.context_menu.as_mut() {
4381 if context_menu.select_prev(cx) {
4382 return;
4383 }
4384 }
4385
4386 if matches!(self.mode, EditorMode::SingleLine) {
4387 cx.propagate_action();
4388 return;
4389 }
4390
4391 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4392 let line_mode = s.line_mode;
4393 s.move_with(|map, selection| {
4394 if !selection.is_empty() && !line_mode {
4395 selection.goal = SelectionGoal::None;
4396 }
4397 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4398 selection.collapse_to(cursor, goal);
4399 });
4400 })
4401 }
4402
4403 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4404 if self.take_rename(true, cx).is_some() {
4405 return;
4406 }
4407
4408 if self
4409 .context_menu
4410 .as_mut()
4411 .map(|menu| menu.select_first(cx))
4412 .unwrap_or(false)
4413 {
4414 return;
4415 }
4416
4417 if matches!(self.mode, EditorMode::SingleLine) {
4418 cx.propagate_action();
4419 return;
4420 }
4421
4422 let row_count = if let Some(row_count) = self.visible_line_count() {
4423 row_count as u32 - 1
4424 } else {
4425 return;
4426 };
4427
4428 let autoscroll = if action.center_cursor {
4429 Autoscroll::center()
4430 } else {
4431 Autoscroll::fit()
4432 };
4433
4434 self.change_selections(Some(autoscroll), cx, |s| {
4435 let line_mode = s.line_mode;
4436 s.move_with(|map, selection| {
4437 if !selection.is_empty() && !line_mode {
4438 selection.goal = SelectionGoal::None;
4439 }
4440 let (cursor, goal) =
4441 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4442 selection.collapse_to(cursor, goal);
4443 });
4444 });
4445 }
4446
4447 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4448 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4449 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4450 })
4451 }
4452
4453 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4454 self.take_rename(true, cx);
4455
4456 if let Some(context_menu) = self.context_menu.as_mut() {
4457 if context_menu.select_next(cx) {
4458 return;
4459 }
4460 }
4461
4462 if self.mode == EditorMode::SingleLine {
4463 cx.propagate_action();
4464 return;
4465 }
4466
4467 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4468 let line_mode = s.line_mode;
4469 s.move_with(|map, selection| {
4470 if !selection.is_empty() && !line_mode {
4471 selection.goal = SelectionGoal::None;
4472 }
4473 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4474 selection.collapse_to(cursor, goal);
4475 });
4476 });
4477 }
4478
4479 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4480 if self.take_rename(true, cx).is_some() {
4481 return;
4482 }
4483
4484 if self
4485 .context_menu
4486 .as_mut()
4487 .map(|menu| menu.select_last(cx))
4488 .unwrap_or(false)
4489 {
4490 return;
4491 }
4492
4493 if matches!(self.mode, EditorMode::SingleLine) {
4494 cx.propagate_action();
4495 return;
4496 }
4497
4498 let row_count = if let Some(row_count) = self.visible_line_count() {
4499 row_count as u32 - 1
4500 } else {
4501 return;
4502 };
4503
4504 let autoscroll = if action.center_cursor {
4505 Autoscroll::center()
4506 } else {
4507 Autoscroll::fit()
4508 };
4509
4510 self.change_selections(Some(autoscroll), cx, |s| {
4511 let line_mode = s.line_mode;
4512 s.move_with(|map, selection| {
4513 if !selection.is_empty() && !line_mode {
4514 selection.goal = SelectionGoal::None;
4515 }
4516 let (cursor, goal) =
4517 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4518 selection.collapse_to(cursor, goal);
4519 });
4520 });
4521 }
4522
4523 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4524 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4525 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4526 });
4527 }
4528
4529 pub fn move_to_previous_word_start(
4530 &mut self,
4531 _: &MoveToPreviousWordStart,
4532 cx: &mut ViewContext<Self>,
4533 ) {
4534 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4535 s.move_cursors_with(|map, head, _| {
4536 (
4537 movement::previous_word_start(map, head),
4538 SelectionGoal::None,
4539 )
4540 });
4541 })
4542 }
4543
4544 pub fn move_to_previous_subword_start(
4545 &mut self,
4546 _: &MoveToPreviousSubwordStart,
4547 cx: &mut ViewContext<Self>,
4548 ) {
4549 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4550 s.move_cursors_with(|map, head, _| {
4551 (
4552 movement::previous_subword_start(map, head),
4553 SelectionGoal::None,
4554 )
4555 });
4556 })
4557 }
4558
4559 pub fn select_to_previous_word_start(
4560 &mut self,
4561 _: &SelectToPreviousWordStart,
4562 cx: &mut ViewContext<Self>,
4563 ) {
4564 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4565 s.move_heads_with(|map, head, _| {
4566 (
4567 movement::previous_word_start(map, head),
4568 SelectionGoal::None,
4569 )
4570 });
4571 })
4572 }
4573
4574 pub fn select_to_previous_subword_start(
4575 &mut self,
4576 _: &SelectToPreviousSubwordStart,
4577 cx: &mut ViewContext<Self>,
4578 ) {
4579 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4580 s.move_heads_with(|map, head, _| {
4581 (
4582 movement::previous_subword_start(map, head),
4583 SelectionGoal::None,
4584 )
4585 });
4586 })
4587 }
4588
4589 pub fn delete_to_previous_word_start(
4590 &mut self,
4591 _: &DeleteToPreviousWordStart,
4592 cx: &mut ViewContext<Self>,
4593 ) {
4594 self.transact(cx, |this, cx| {
4595 this.select_autoclose_pair(cx);
4596 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4597 let line_mode = s.line_mode;
4598 s.move_with(|map, selection| {
4599 if selection.is_empty() && !line_mode {
4600 let cursor = movement::previous_word_start(map, selection.head());
4601 selection.set_head(cursor, SelectionGoal::None);
4602 }
4603 });
4604 });
4605 this.insert("", cx);
4606 });
4607 }
4608
4609 pub fn delete_to_previous_subword_start(
4610 &mut self,
4611 _: &DeleteToPreviousSubwordStart,
4612 cx: &mut ViewContext<Self>,
4613 ) {
4614 self.transact(cx, |this, cx| {
4615 this.select_autoclose_pair(cx);
4616 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4617 let line_mode = s.line_mode;
4618 s.move_with(|map, selection| {
4619 if selection.is_empty() && !line_mode {
4620 let cursor = movement::previous_subword_start(map, selection.head());
4621 selection.set_head(cursor, SelectionGoal::None);
4622 }
4623 });
4624 });
4625 this.insert("", cx);
4626 });
4627 }
4628
4629 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4630 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4631 s.move_cursors_with(|map, head, _| {
4632 (movement::next_word_end(map, head), SelectionGoal::None)
4633 });
4634 })
4635 }
4636
4637 pub fn move_to_next_subword_end(
4638 &mut self,
4639 _: &MoveToNextSubwordEnd,
4640 cx: &mut ViewContext<Self>,
4641 ) {
4642 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4643 s.move_cursors_with(|map, head, _| {
4644 (movement::next_subword_end(map, head), SelectionGoal::None)
4645 });
4646 })
4647 }
4648
4649 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4650 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4651 s.move_heads_with(|map, head, _| {
4652 (movement::next_word_end(map, head), SelectionGoal::None)
4653 });
4654 })
4655 }
4656
4657 pub fn select_to_next_subword_end(
4658 &mut self,
4659 _: &SelectToNextSubwordEnd,
4660 cx: &mut ViewContext<Self>,
4661 ) {
4662 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4663 s.move_heads_with(|map, head, _| {
4664 (movement::next_subword_end(map, head), SelectionGoal::None)
4665 });
4666 })
4667 }
4668
4669 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4670 self.transact(cx, |this, cx| {
4671 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4672 let line_mode = s.line_mode;
4673 s.move_with(|map, selection| {
4674 if selection.is_empty() && !line_mode {
4675 let cursor = movement::next_word_end(map, selection.head());
4676 selection.set_head(cursor, SelectionGoal::None);
4677 }
4678 });
4679 });
4680 this.insert("", cx);
4681 });
4682 }
4683
4684 pub fn delete_to_next_subword_end(
4685 &mut self,
4686 _: &DeleteToNextSubwordEnd,
4687 cx: &mut ViewContext<Self>,
4688 ) {
4689 self.transact(cx, |this, cx| {
4690 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4691 s.move_with(|map, selection| {
4692 if selection.is_empty() {
4693 let cursor = movement::next_subword_end(map, selection.head());
4694 selection.set_head(cursor, SelectionGoal::None);
4695 }
4696 });
4697 });
4698 this.insert("", cx);
4699 });
4700 }
4701
4702 pub fn move_to_beginning_of_line(
4703 &mut self,
4704 _: &MoveToBeginningOfLine,
4705 cx: &mut ViewContext<Self>,
4706 ) {
4707 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4708 s.move_cursors_with(|map, head, _| {
4709 (
4710 movement::indented_line_beginning(map, head, true),
4711 SelectionGoal::None,
4712 )
4713 });
4714 })
4715 }
4716
4717 pub fn select_to_beginning_of_line(
4718 &mut self,
4719 action: &SelectToBeginningOfLine,
4720 cx: &mut ViewContext<Self>,
4721 ) {
4722 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4723 s.move_heads_with(|map, head, _| {
4724 (
4725 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4726 SelectionGoal::None,
4727 )
4728 });
4729 });
4730 }
4731
4732 pub fn delete_to_beginning_of_line(
4733 &mut self,
4734 _: &DeleteToBeginningOfLine,
4735 cx: &mut ViewContext<Self>,
4736 ) {
4737 self.transact(cx, |this, cx| {
4738 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4739 s.move_with(|_, selection| {
4740 selection.reversed = true;
4741 });
4742 });
4743
4744 this.select_to_beginning_of_line(
4745 &SelectToBeginningOfLine {
4746 stop_at_soft_wraps: false,
4747 },
4748 cx,
4749 );
4750 this.backspace(&Backspace, cx);
4751 });
4752 }
4753
4754 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4755 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4756 s.move_cursors_with(|map, head, _| {
4757 (movement::line_end(map, head, true), SelectionGoal::None)
4758 });
4759 })
4760 }
4761
4762 pub fn select_to_end_of_line(
4763 &mut self,
4764 action: &SelectToEndOfLine,
4765 cx: &mut ViewContext<Self>,
4766 ) {
4767 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4768 s.move_heads_with(|map, head, _| {
4769 (
4770 movement::line_end(map, head, action.stop_at_soft_wraps),
4771 SelectionGoal::None,
4772 )
4773 });
4774 })
4775 }
4776
4777 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, 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.delete(&Delete, cx);
4786 });
4787 }
4788
4789 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4790 self.transact(cx, |this, cx| {
4791 this.select_to_end_of_line(
4792 &SelectToEndOfLine {
4793 stop_at_soft_wraps: false,
4794 },
4795 cx,
4796 );
4797 this.cut(&Cut, cx);
4798 });
4799 }
4800
4801 pub fn move_to_start_of_paragraph(
4802 &mut self,
4803 _: &MoveToStartOfParagraph,
4804 cx: &mut ViewContext<Self>,
4805 ) {
4806 if matches!(self.mode, EditorMode::SingleLine) {
4807 cx.propagate_action();
4808 return;
4809 }
4810
4811 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4812 s.move_with(|map, selection| {
4813 selection.collapse_to(
4814 movement::start_of_paragraph(map, selection.head()),
4815 SelectionGoal::None,
4816 )
4817 });
4818 })
4819 }
4820
4821 pub fn move_to_end_of_paragraph(
4822 &mut self,
4823 _: &MoveToEndOfParagraph,
4824 cx: &mut ViewContext<Self>,
4825 ) {
4826 if matches!(self.mode, EditorMode::SingleLine) {
4827 cx.propagate_action();
4828 return;
4829 }
4830
4831 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4832 s.move_with(|map, selection| {
4833 selection.collapse_to(
4834 movement::end_of_paragraph(map, selection.head()),
4835 SelectionGoal::None,
4836 )
4837 });
4838 })
4839 }
4840
4841 pub fn select_to_start_of_paragraph(
4842 &mut self,
4843 _: &SelectToStartOfParagraph,
4844 cx: &mut ViewContext<Self>,
4845 ) {
4846 if matches!(self.mode, EditorMode::SingleLine) {
4847 cx.propagate_action();
4848 return;
4849 }
4850
4851 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4852 s.move_heads_with(|map, head, _| {
4853 (movement::start_of_paragraph(map, head), SelectionGoal::None)
4854 });
4855 })
4856 }
4857
4858 pub fn select_to_end_of_paragraph(
4859 &mut self,
4860 _: &SelectToEndOfParagraph,
4861 cx: &mut ViewContext<Self>,
4862 ) {
4863 if matches!(self.mode, EditorMode::SingleLine) {
4864 cx.propagate_action();
4865 return;
4866 }
4867
4868 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4869 s.move_heads_with(|map, head, _| {
4870 (movement::end_of_paragraph(map, head), SelectionGoal::None)
4871 });
4872 })
4873 }
4874
4875 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4876 if matches!(self.mode, EditorMode::SingleLine) {
4877 cx.propagate_action();
4878 return;
4879 }
4880
4881 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4882 s.select_ranges(vec![0..0]);
4883 });
4884 }
4885
4886 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4887 let mut selection = self.selections.last::<Point>(cx);
4888 selection.set_head(Point::zero(), SelectionGoal::None);
4889
4890 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4891 s.select(vec![selection]);
4892 });
4893 }
4894
4895 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4896 if matches!(self.mode, EditorMode::SingleLine) {
4897 cx.propagate_action();
4898 return;
4899 }
4900
4901 let cursor = self.buffer.read(cx).read(cx).len();
4902 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4903 s.select_ranges(vec![cursor..cursor])
4904 });
4905 }
4906
4907 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4908 self.nav_history = nav_history;
4909 }
4910
4911 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4912 self.nav_history.as_ref()
4913 }
4914
4915 fn push_to_nav_history(
4916 &self,
4917 cursor_anchor: Anchor,
4918 new_position: Option<Point>,
4919 cx: &mut ViewContext<Self>,
4920 ) {
4921 if let Some(nav_history) = &self.nav_history {
4922 let buffer = self.buffer.read(cx).read(cx);
4923 let cursor_position = cursor_anchor.to_point(&buffer);
4924 let scroll_state = self.scroll_manager.anchor();
4925 let scroll_top_row = scroll_state.top_row(&buffer);
4926 drop(buffer);
4927
4928 if let Some(new_position) = new_position {
4929 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
4930 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4931 return;
4932 }
4933 }
4934
4935 nav_history.push(
4936 Some(NavigationData {
4937 cursor_anchor,
4938 cursor_position,
4939 scroll_anchor: scroll_state,
4940 scroll_top_row,
4941 }),
4942 cx,
4943 );
4944 }
4945 }
4946
4947 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4948 let buffer = self.buffer.read(cx).snapshot(cx);
4949 let mut selection = self.selections.first::<usize>(cx);
4950 selection.set_head(buffer.len(), SelectionGoal::None);
4951 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4952 s.select(vec![selection]);
4953 });
4954 }
4955
4956 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4957 let end = self.buffer.read(cx).read(cx).len();
4958 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4959 s.select_ranges(vec![0..end]);
4960 });
4961 }
4962
4963 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4964 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4965 let mut selections = self.selections.all::<Point>(cx);
4966 let max_point = display_map.buffer_snapshot.max_point();
4967 for selection in &mut selections {
4968 let rows = selection.spanned_rows(true, &display_map);
4969 selection.start = Point::new(rows.start, 0);
4970 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4971 selection.reversed = false;
4972 }
4973 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4974 s.select(selections);
4975 });
4976 }
4977
4978 pub fn split_selection_into_lines(
4979 &mut self,
4980 _: &SplitSelectionIntoLines,
4981 cx: &mut ViewContext<Self>,
4982 ) {
4983 let mut to_unfold = Vec::new();
4984 let mut new_selection_ranges = Vec::new();
4985 {
4986 let selections = self.selections.all::<Point>(cx);
4987 let buffer = self.buffer.read(cx).read(cx);
4988 for selection in selections {
4989 for row in selection.start.row..selection.end.row {
4990 let cursor = Point::new(row, buffer.line_len(row));
4991 new_selection_ranges.push(cursor..cursor);
4992 }
4993 new_selection_ranges.push(selection.end..selection.end);
4994 to_unfold.push(selection.start..selection.end);
4995 }
4996 }
4997 self.unfold_ranges(to_unfold, true, true, cx);
4998 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4999 s.select_ranges(new_selection_ranges);
5000 });
5001 }
5002
5003 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5004 self.add_selection(true, cx);
5005 }
5006
5007 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5008 self.add_selection(false, cx);
5009 }
5010
5011 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5012 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5013 let mut selections = self.selections.all::<Point>(cx);
5014 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5015 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5016 let range = oldest_selection.display_range(&display_map).sorted();
5017 let columns = cmp::min(range.start.column(), range.end.column())
5018 ..cmp::max(range.start.column(), range.end.column());
5019
5020 selections.clear();
5021 let mut stack = Vec::new();
5022 for row in range.start.row()..=range.end.row() {
5023 if let Some(selection) = self.selections.build_columnar_selection(
5024 &display_map,
5025 row,
5026 &columns,
5027 oldest_selection.reversed,
5028 ) {
5029 stack.push(selection.id);
5030 selections.push(selection);
5031 }
5032 }
5033
5034 if above {
5035 stack.reverse();
5036 }
5037
5038 AddSelectionsState { above, stack }
5039 });
5040
5041 let last_added_selection = *state.stack.last().unwrap();
5042 let mut new_selections = Vec::new();
5043 if above == state.above {
5044 let end_row = if above {
5045 0
5046 } else {
5047 display_map.max_point().row()
5048 };
5049
5050 'outer: for selection in selections {
5051 if selection.id == last_added_selection {
5052 let range = selection.display_range(&display_map).sorted();
5053 debug_assert_eq!(range.start.row(), range.end.row());
5054 let mut row = range.start.row();
5055 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5056 {
5057 start..end
5058 } else {
5059 cmp::min(range.start.column(), range.end.column())
5060 ..cmp::max(range.start.column(), range.end.column())
5061 };
5062
5063 while row != end_row {
5064 if above {
5065 row -= 1;
5066 } else {
5067 row += 1;
5068 }
5069
5070 if let Some(new_selection) = self.selections.build_columnar_selection(
5071 &display_map,
5072 row,
5073 &columns,
5074 selection.reversed,
5075 ) {
5076 state.stack.push(new_selection.id);
5077 if above {
5078 new_selections.push(new_selection);
5079 new_selections.push(selection);
5080 } else {
5081 new_selections.push(selection);
5082 new_selections.push(new_selection);
5083 }
5084
5085 continue 'outer;
5086 }
5087 }
5088 }
5089
5090 new_selections.push(selection);
5091 }
5092 } else {
5093 new_selections = selections;
5094 new_selections.retain(|s| s.id != last_added_selection);
5095 state.stack.pop();
5096 }
5097
5098 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5099 s.select(new_selections);
5100 });
5101 if state.stack.len() > 1 {
5102 self.add_selections_state = Some(state);
5103 }
5104 }
5105
5106 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5107 self.push_to_selection_history();
5108 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5109 let buffer = &display_map.buffer_snapshot;
5110 let mut selections = self.selections.all::<usize>(cx);
5111 if let Some(mut select_next_state) = self.select_next_state.take() {
5112 let query = &select_next_state.query;
5113 if !select_next_state.done {
5114 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5115 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5116 let mut next_selected_range = None;
5117
5118 let bytes_after_last_selection =
5119 buffer.bytes_in_range(last_selection.end..buffer.len());
5120 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5121 let query_matches = query
5122 .stream_find_iter(bytes_after_last_selection)
5123 .map(|result| (last_selection.end, result))
5124 .chain(
5125 query
5126 .stream_find_iter(bytes_before_first_selection)
5127 .map(|result| (0, result)),
5128 );
5129 for (start_offset, query_match) in query_matches {
5130 let query_match = query_match.unwrap(); // can only fail due to I/O
5131 let offset_range =
5132 start_offset + query_match.start()..start_offset + query_match.end();
5133 let display_range = offset_range.start.to_display_point(&display_map)
5134 ..offset_range.end.to_display_point(&display_map);
5135
5136 if !select_next_state.wordwise
5137 || (!movement::is_inside_word(&display_map, display_range.start)
5138 && !movement::is_inside_word(&display_map, display_range.end))
5139 {
5140 next_selected_range = Some(offset_range);
5141 break;
5142 }
5143 }
5144
5145 if let Some(next_selected_range) = next_selected_range {
5146 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5147 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5148 if action.replace_newest {
5149 s.delete(s.newest_anchor().id);
5150 }
5151 s.insert_range(next_selected_range);
5152 });
5153 } else {
5154 select_next_state.done = true;
5155 }
5156 }
5157
5158 self.select_next_state = Some(select_next_state);
5159 } else if selections.len() == 1 {
5160 let selection = selections.last_mut().unwrap();
5161 if selection.start == selection.end {
5162 let word_range = movement::surrounding_word(
5163 &display_map,
5164 selection.start.to_display_point(&display_map),
5165 );
5166 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5167 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5168 selection.goal = SelectionGoal::None;
5169 selection.reversed = false;
5170
5171 let query = buffer
5172 .text_for_range(selection.start..selection.end)
5173 .collect::<String>();
5174 let select_state = SelectNextState {
5175 query: AhoCorasick::new_auto_configured(&[query]),
5176 wordwise: true,
5177 done: false,
5178 };
5179 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5180 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5181 s.select(selections);
5182 });
5183 self.select_next_state = Some(select_state);
5184 } else {
5185 let query = buffer
5186 .text_for_range(selection.start..selection.end)
5187 .collect::<String>();
5188 self.select_next_state = Some(SelectNextState {
5189 query: AhoCorasick::new_auto_configured(&[query]),
5190 wordwise: false,
5191 done: false,
5192 });
5193 self.select_next(action, cx);
5194 }
5195 }
5196 }
5197
5198 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5199 self.transact(cx, |this, cx| {
5200 let mut selections = this.selections.all::<Point>(cx);
5201 let mut edits = Vec::new();
5202 let mut selection_edit_ranges = Vec::new();
5203 let mut last_toggled_row = None;
5204 let snapshot = this.buffer.read(cx).read(cx);
5205 let empty_str: Arc<str> = "".into();
5206 let mut suffixes_inserted = Vec::new();
5207
5208 fn comment_prefix_range(
5209 snapshot: &MultiBufferSnapshot,
5210 row: u32,
5211 comment_prefix: &str,
5212 comment_prefix_whitespace: &str,
5213 ) -> Range<Point> {
5214 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5215
5216 let mut line_bytes = snapshot
5217 .bytes_in_range(start..snapshot.max_point())
5218 .flatten()
5219 .copied();
5220
5221 // If this line currently begins with the line comment prefix, then record
5222 // the range containing the prefix.
5223 if line_bytes
5224 .by_ref()
5225 .take(comment_prefix.len())
5226 .eq(comment_prefix.bytes())
5227 {
5228 // Include any whitespace that matches the comment prefix.
5229 let matching_whitespace_len = line_bytes
5230 .zip(comment_prefix_whitespace.bytes())
5231 .take_while(|(a, b)| a == b)
5232 .count() as u32;
5233 let end = Point::new(
5234 start.row,
5235 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5236 );
5237 start..end
5238 } else {
5239 start..start
5240 }
5241 }
5242
5243 fn comment_suffix_range(
5244 snapshot: &MultiBufferSnapshot,
5245 row: u32,
5246 comment_suffix: &str,
5247 comment_suffix_has_leading_space: bool,
5248 ) -> Range<Point> {
5249 let end = Point::new(row, snapshot.line_len(row));
5250 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5251
5252 let mut line_end_bytes = snapshot
5253 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5254 .flatten()
5255 .copied();
5256
5257 let leading_space_len = if suffix_start_column > 0
5258 && line_end_bytes.next() == Some(b' ')
5259 && comment_suffix_has_leading_space
5260 {
5261 1
5262 } else {
5263 0
5264 };
5265
5266 // If this line currently begins with the line comment prefix, then record
5267 // the range containing the prefix.
5268 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5269 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5270 start..end
5271 } else {
5272 end..end
5273 }
5274 }
5275
5276 // TODO: Handle selections that cross excerpts
5277 for selection in &mut selections {
5278 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5279 let language = if let Some(language) =
5280 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5281 {
5282 language
5283 } else {
5284 continue;
5285 };
5286
5287 selection_edit_ranges.clear();
5288
5289 // If multiple selections contain a given row, avoid processing that
5290 // row more than once.
5291 let mut start_row = selection.start.row;
5292 if last_toggled_row == Some(start_row) {
5293 start_row += 1;
5294 }
5295 let end_row =
5296 if selection.end.row > selection.start.row && selection.end.column == 0 {
5297 selection.end.row - 1
5298 } else {
5299 selection.end.row
5300 };
5301 last_toggled_row = Some(end_row);
5302
5303 if start_row > end_row {
5304 continue;
5305 }
5306
5307 // If the language has line comments, toggle those.
5308 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5309 // Split the comment prefix's trailing whitespace into a separate string,
5310 // as that portion won't be used for detecting if a line is a comment.
5311 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5312 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5313 let mut all_selection_lines_are_comments = true;
5314
5315 for row in start_row..=end_row {
5316 if snapshot.is_line_blank(row) {
5317 continue;
5318 }
5319
5320 let prefix_range = comment_prefix_range(
5321 snapshot.deref(),
5322 row,
5323 comment_prefix,
5324 comment_prefix_whitespace,
5325 );
5326 if prefix_range.is_empty() {
5327 all_selection_lines_are_comments = false;
5328 }
5329 selection_edit_ranges.push(prefix_range);
5330 }
5331
5332 if all_selection_lines_are_comments {
5333 edits.extend(
5334 selection_edit_ranges
5335 .iter()
5336 .cloned()
5337 .map(|range| (range, empty_str.clone())),
5338 );
5339 } else {
5340 let min_column = selection_edit_ranges
5341 .iter()
5342 .map(|r| r.start.column)
5343 .min()
5344 .unwrap_or(0);
5345 edits.extend(selection_edit_ranges.iter().map(|range| {
5346 let position = Point::new(range.start.row, min_column);
5347 (position..position, full_comment_prefix.clone())
5348 }));
5349 }
5350 } else if let Some((full_comment_prefix, comment_suffix)) =
5351 language.block_comment_delimiters()
5352 {
5353 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5354 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5355 let prefix_range = comment_prefix_range(
5356 snapshot.deref(),
5357 start_row,
5358 comment_prefix,
5359 comment_prefix_whitespace,
5360 );
5361 let suffix_range = comment_suffix_range(
5362 snapshot.deref(),
5363 end_row,
5364 comment_suffix.trim_start_matches(' '),
5365 comment_suffix.starts_with(' '),
5366 );
5367
5368 if prefix_range.is_empty() || suffix_range.is_empty() {
5369 edits.push((
5370 prefix_range.start..prefix_range.start,
5371 full_comment_prefix.clone(),
5372 ));
5373 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5374 suffixes_inserted.push((end_row, comment_suffix.len()));
5375 } else {
5376 edits.push((prefix_range, empty_str.clone()));
5377 edits.push((suffix_range, empty_str.clone()));
5378 }
5379 } else {
5380 continue;
5381 }
5382 }
5383
5384 drop(snapshot);
5385 this.buffer.update(cx, |buffer, cx| {
5386 buffer.edit(edits, None, cx);
5387 });
5388
5389 // Adjust selections so that they end before any comment suffixes that
5390 // were inserted.
5391 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5392 let mut selections = this.selections.all::<Point>(cx);
5393 let snapshot = this.buffer.read(cx).read(cx);
5394 for selection in &mut selections {
5395 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5396 match row.cmp(&selection.end.row) {
5397 Ordering::Less => {
5398 suffixes_inserted.next();
5399 continue;
5400 }
5401 Ordering::Greater => break,
5402 Ordering::Equal => {
5403 if selection.end.column == snapshot.line_len(row) {
5404 if selection.is_empty() {
5405 selection.start.column -= suffix_len as u32;
5406 }
5407 selection.end.column -= suffix_len as u32;
5408 }
5409 break;
5410 }
5411 }
5412 }
5413 }
5414
5415 drop(snapshot);
5416 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5417
5418 let selections = this.selections.all::<Point>(cx);
5419 let selections_on_single_row = selections.windows(2).all(|selections| {
5420 selections[0].start.row == selections[1].start.row
5421 && selections[0].end.row == selections[1].end.row
5422 && selections[0].start.row == selections[0].end.row
5423 });
5424 let selections_selecting = selections
5425 .iter()
5426 .any(|selection| selection.start != selection.end);
5427 let advance_downwards = action.advance_downwards
5428 && selections_on_single_row
5429 && !selections_selecting
5430 && this.mode != EditorMode::SingleLine;
5431
5432 if advance_downwards {
5433 let snapshot = this.buffer.read(cx).snapshot(cx);
5434
5435 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5436 s.move_cursors_with(|display_snapshot, display_point, _| {
5437 let mut point = display_point.to_point(display_snapshot);
5438 point.row += 1;
5439 point = snapshot.clip_point(point, Bias::Left);
5440 let display_point = point.to_display_point(display_snapshot);
5441 (display_point, SelectionGoal::Column(display_point.column()))
5442 })
5443 });
5444 }
5445 });
5446 }
5447
5448 pub fn select_larger_syntax_node(
5449 &mut self,
5450 _: &SelectLargerSyntaxNode,
5451 cx: &mut ViewContext<Self>,
5452 ) {
5453 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5454 let buffer = self.buffer.read(cx).snapshot(cx);
5455 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5456
5457 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5458 let mut selected_larger_node = false;
5459 let new_selections = old_selections
5460 .iter()
5461 .map(|selection| {
5462 let old_range = selection.start..selection.end;
5463 let mut new_range = old_range.clone();
5464 while let Some(containing_range) =
5465 buffer.range_for_syntax_ancestor(new_range.clone())
5466 {
5467 new_range = containing_range;
5468 if !display_map.intersects_fold(new_range.start)
5469 && !display_map.intersects_fold(new_range.end)
5470 {
5471 break;
5472 }
5473 }
5474
5475 selected_larger_node |= new_range != old_range;
5476 Selection {
5477 id: selection.id,
5478 start: new_range.start,
5479 end: new_range.end,
5480 goal: SelectionGoal::None,
5481 reversed: selection.reversed,
5482 }
5483 })
5484 .collect::<Vec<_>>();
5485
5486 if selected_larger_node {
5487 stack.push(old_selections);
5488 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5489 s.select(new_selections);
5490 });
5491 }
5492 self.select_larger_syntax_node_stack = stack;
5493 }
5494
5495 pub fn select_smaller_syntax_node(
5496 &mut self,
5497 _: &SelectSmallerSyntaxNode,
5498 cx: &mut ViewContext<Self>,
5499 ) {
5500 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5501 if let Some(selections) = stack.pop() {
5502 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5503 s.select(selections.to_vec());
5504 });
5505 }
5506 self.select_larger_syntax_node_stack = stack;
5507 }
5508
5509 pub fn move_to_enclosing_bracket(
5510 &mut self,
5511 _: &MoveToEnclosingBracket,
5512 cx: &mut ViewContext<Self>,
5513 ) {
5514 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5515 s.move_offsets_with(|snapshot, selection| {
5516 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5517 return;
5518 };
5519
5520 let mut best_length = usize::MAX;
5521 let mut best_inside = false;
5522 let mut best_in_bracket_range = false;
5523 let mut best_destination = None;
5524 for (open, close) in enclosing_bracket_ranges {
5525 let close = close.to_inclusive();
5526 let length = close.end() - open.start;
5527 let inside = selection.start >= open.end && selection.end <= *close.start();
5528 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5529
5530 // If best is next to a bracket and current isn't, skip
5531 if !in_bracket_range && best_in_bracket_range {
5532 continue;
5533 }
5534
5535 // Prefer smaller lengths unless best is inside and current isn't
5536 if length > best_length && (best_inside || !inside) {
5537 continue;
5538 }
5539
5540 best_length = length;
5541 best_inside = inside;
5542 best_in_bracket_range = in_bracket_range;
5543 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5544 if inside {
5545 open.end
5546 } else {
5547 open.start
5548 }
5549 } else {
5550 if inside {
5551 *close.start()
5552 } else {
5553 *close.end()
5554 }
5555 });
5556 }
5557
5558 if let Some(destination) = best_destination {
5559 selection.collapse_to(destination, SelectionGoal::None);
5560 }
5561 })
5562 });
5563 }
5564
5565 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5566 self.end_selection(cx);
5567 self.selection_history.mode = SelectionHistoryMode::Undoing;
5568 if let Some(entry) = self.selection_history.undo_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 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5578 self.end_selection(cx);
5579 self.selection_history.mode = SelectionHistoryMode::Redoing;
5580 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5581 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5582 self.select_next_state = entry.select_next_state;
5583 self.add_selections_state = entry.add_selections_state;
5584 self.request_autoscroll(Autoscroll::newest(), cx);
5585 }
5586 self.selection_history.mode = SelectionHistoryMode::Normal;
5587 }
5588
5589 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5590 self.go_to_diagnostic_impl(Direction::Next, cx)
5591 }
5592
5593 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5594 self.go_to_diagnostic_impl(Direction::Prev, cx)
5595 }
5596
5597 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5598 let buffer = self.buffer.read(cx).snapshot(cx);
5599 let selection = self.selections.newest::<usize>(cx);
5600
5601 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5602 if direction == Direction::Next {
5603 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5604 let (group_id, jump_to) = popover.activation_info();
5605 if self.activate_diagnostics(group_id, cx) {
5606 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5607 let mut new_selection = s.newest_anchor().clone();
5608 new_selection.collapse_to(jump_to, SelectionGoal::None);
5609 s.select_anchors(vec![new_selection.clone()]);
5610 });
5611 }
5612 return;
5613 }
5614 }
5615
5616 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5617 active_diagnostics
5618 .primary_range
5619 .to_offset(&buffer)
5620 .to_inclusive()
5621 });
5622 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5623 if active_primary_range.contains(&selection.head()) {
5624 *active_primary_range.end()
5625 } else {
5626 selection.head()
5627 }
5628 } else {
5629 selection.head()
5630 };
5631
5632 loop {
5633 let mut diagnostics = if direction == Direction::Prev {
5634 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5635 } else {
5636 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5637 };
5638 let group = diagnostics.find_map(|entry| {
5639 if entry.diagnostic.is_primary
5640 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5641 && !entry.range.is_empty()
5642 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5643 {
5644 Some((entry.range, entry.diagnostic.group_id))
5645 } else {
5646 None
5647 }
5648 });
5649
5650 if let Some((primary_range, group_id)) = group {
5651 if self.activate_diagnostics(group_id, cx) {
5652 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5653 s.select(vec![Selection {
5654 id: selection.id,
5655 start: primary_range.start,
5656 end: primary_range.start,
5657 reversed: false,
5658 goal: SelectionGoal::None,
5659 }]);
5660 });
5661 }
5662 break;
5663 } else {
5664 // Cycle around to the start of the buffer, potentially moving back to the start of
5665 // the currently active diagnostic.
5666 active_primary_range.take();
5667 if direction == Direction::Prev {
5668 if search_start == buffer.len() {
5669 break;
5670 } else {
5671 search_start = buffer.len();
5672 }
5673 } else if search_start == 0 {
5674 break;
5675 } else {
5676 search_start = 0;
5677 }
5678 }
5679 }
5680 }
5681
5682 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5683 let snapshot = self
5684 .display_map
5685 .update(cx, |display_map, cx| display_map.snapshot(cx));
5686 let selection = self.selections.newest::<Point>(cx);
5687
5688 if !self.seek_in_direction(
5689 &snapshot,
5690 selection.head(),
5691 false,
5692 snapshot
5693 .buffer_snapshot
5694 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
5695 cx,
5696 ) {
5697 let wrapped_point = Point::zero();
5698 self.seek_in_direction(
5699 &snapshot,
5700 wrapped_point,
5701 true,
5702 snapshot
5703 .buffer_snapshot
5704 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
5705 cx,
5706 );
5707 }
5708 }
5709
5710 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5711 let snapshot = self
5712 .display_map
5713 .update(cx, |display_map, cx| display_map.snapshot(cx));
5714 let selection = self.selections.newest::<Point>(cx);
5715
5716 if !self.seek_in_direction(
5717 &snapshot,
5718 selection.head(),
5719 false,
5720 snapshot
5721 .buffer_snapshot
5722 .git_diff_hunks_in_range_rev(0..selection.head().row),
5723 cx,
5724 ) {
5725 let wrapped_point = snapshot.buffer_snapshot.max_point();
5726 self.seek_in_direction(
5727 &snapshot,
5728 wrapped_point,
5729 true,
5730 snapshot
5731 .buffer_snapshot
5732 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
5733 cx,
5734 );
5735 }
5736 }
5737
5738 fn seek_in_direction(
5739 &mut self,
5740 snapshot: &DisplaySnapshot,
5741 initial_point: Point,
5742 is_wrapped: bool,
5743 hunks: impl Iterator<Item = DiffHunk<u32>>,
5744 cx: &mut ViewContext<Editor>,
5745 ) -> bool {
5746 let display_point = initial_point.to_display_point(snapshot);
5747 let mut hunks = hunks
5748 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5749 .skip_while(|hunk| {
5750 if is_wrapped {
5751 false
5752 } else {
5753 hunk.contains_display_row(display_point.row())
5754 }
5755 })
5756 .dedup();
5757
5758 if let Some(hunk) = hunks.next() {
5759 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5760 let row = hunk.start_display_row();
5761 let point = DisplayPoint::new(row, 0);
5762 s.select_display_ranges([point..point]);
5763 });
5764
5765 true
5766 } else {
5767 false
5768 }
5769 }
5770
5771 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
5772 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
5773 }
5774
5775 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
5776 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
5777 }
5778
5779 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
5780 let Some(workspace) = self.workspace(cx) else { return };
5781 let buffer = self.buffer.read(cx);
5782 let head = self.selections.newest::<usize>(cx).head();
5783 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5784 text_anchor
5785 } else {
5786 return;
5787 };
5788
5789 let project = workspace.read(cx).project().clone();
5790 let definitions = project.update(cx, |project, cx| match kind {
5791 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5792 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5793 });
5794
5795 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
5796 let definitions = definitions.await?;
5797 editor.update(&mut cx, |editor, cx| {
5798 editor.navigate_to_definitions(definitions, cx);
5799 })?;
5800 Ok::<(), anyhow::Error>(())
5801 })
5802 .detach_and_log_err(cx);
5803 }
5804
5805 pub fn navigate_to_definitions(
5806 &mut self,
5807 mut definitions: Vec<LocationLink>,
5808 cx: &mut ViewContext<Editor>,
5809 ) {
5810 let Some(workspace) = self.workspace(cx) else { return };
5811 let pane = workspace.read(cx).active_pane().clone();
5812 // If there is one definition, just open it directly
5813 if definitions.len() == 1 {
5814 let definition = definitions.pop().unwrap();
5815 let range = definition
5816 .target
5817 .range
5818 .to_offset(definition.target.buffer.read(cx));
5819
5820 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
5821 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5822 s.select_ranges([range]);
5823 });
5824 } else {
5825 cx.window_context().defer(move |cx| {
5826 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
5827 workspace.open_project_item(definition.target.buffer.clone(), cx)
5828 });
5829 target_editor.update(cx, |target_editor, cx| {
5830 // When selecting a definition in a different buffer, disable the nav history
5831 // to avoid creating a history entry at the previous cursor location.
5832 pane.update(cx, |pane, _| pane.disable_history());
5833 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5834 s.select_ranges([range]);
5835 });
5836 pane.update(cx, |pane, _| pane.enable_history());
5837 });
5838 });
5839 }
5840 } else if !definitions.is_empty() {
5841 let replica_id = self.replica_id(cx);
5842 cx.window_context().defer(move |cx| {
5843 let title = definitions
5844 .iter()
5845 .find(|definition| definition.origin.is_some())
5846 .and_then(|definition| {
5847 definition.origin.as_ref().map(|origin| {
5848 let buffer = origin.buffer.read(cx);
5849 format!(
5850 "Definitions for {}",
5851 buffer
5852 .text_for_range(origin.range.clone())
5853 .collect::<String>()
5854 )
5855 })
5856 })
5857 .unwrap_or("Definitions".to_owned());
5858 let locations = definitions
5859 .into_iter()
5860 .map(|definition| definition.target)
5861 .collect();
5862 workspace.update(cx, |workspace, cx| {
5863 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5864 });
5865 });
5866 }
5867 }
5868
5869 pub fn find_all_references(
5870 workspace: &mut Workspace,
5871 _: &FindAllReferences,
5872 cx: &mut ViewContext<Workspace>,
5873 ) -> Option<Task<Result<()>>> {
5874 let active_item = workspace.active_item(cx)?;
5875 let editor_handle = active_item.act_as::<Self>(cx)?;
5876
5877 let editor = editor_handle.read(cx);
5878 let buffer = editor.buffer.read(cx);
5879 let head = editor.selections.newest::<usize>(cx).head();
5880 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5881 let replica_id = editor.replica_id(cx);
5882
5883 let project = workspace.project().clone();
5884 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5885 Some(cx.spawn_labeled(
5886 "Finding All References...",
5887 |workspace, mut cx| async move {
5888 let locations = references.await?;
5889 if locations.is_empty() {
5890 return Ok(());
5891 }
5892
5893 workspace.update(&mut cx, |workspace, cx| {
5894 let title = locations
5895 .first()
5896 .as_ref()
5897 .map(|location| {
5898 let buffer = location.buffer.read(cx);
5899 format!(
5900 "References to `{}`",
5901 buffer
5902 .text_for_range(location.range.clone())
5903 .collect::<String>()
5904 )
5905 })
5906 .unwrap();
5907 Self::open_locations_in_multibuffer(
5908 workspace, locations, replica_id, title, cx,
5909 );
5910 })?;
5911
5912 Ok(())
5913 },
5914 ))
5915 }
5916
5917 /// Opens a multibuffer with the given project locations in it
5918 pub fn open_locations_in_multibuffer(
5919 workspace: &mut Workspace,
5920 mut locations: Vec<Location>,
5921 replica_id: ReplicaId,
5922 title: String,
5923 cx: &mut ViewContext<Workspace>,
5924 ) {
5925 // If there are multiple definitions, open them in a multibuffer
5926 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
5927 let mut locations = locations.into_iter().peekable();
5928 let mut ranges_to_highlight = Vec::new();
5929
5930 let excerpt_buffer = cx.add_model(|cx| {
5931 let mut multibuffer = MultiBuffer::new(replica_id);
5932 while let Some(location) = locations.next() {
5933 let buffer = location.buffer.read(cx);
5934 let mut ranges_for_buffer = Vec::new();
5935 let range = location.range.to_offset(buffer);
5936 ranges_for_buffer.push(range.clone());
5937
5938 while let Some(next_location) = locations.peek() {
5939 if next_location.buffer == location.buffer {
5940 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5941 locations.next();
5942 } else {
5943 break;
5944 }
5945 }
5946
5947 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5948 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5949 location.buffer.clone(),
5950 ranges_for_buffer,
5951 1,
5952 cx,
5953 ))
5954 }
5955
5956 multibuffer.with_title(title)
5957 });
5958
5959 let editor = cx.add_view(|cx| {
5960 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5961 });
5962 editor.update(cx, |editor, cx| {
5963 editor.highlight_background::<Self>(
5964 ranges_to_highlight,
5965 |theme| theme.editor.highlighted_line_background,
5966 cx,
5967 );
5968 });
5969 workspace.add_item(Box::new(editor), cx);
5970 }
5971
5972 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5973 use language::ToOffset as _;
5974
5975 let project = self.project.clone()?;
5976 let selection = self.selections.newest_anchor().clone();
5977 let (cursor_buffer, cursor_buffer_position) = self
5978 .buffer
5979 .read(cx)
5980 .text_anchor_for_position(selection.head(), cx)?;
5981 let (tail_buffer, _) = self
5982 .buffer
5983 .read(cx)
5984 .text_anchor_for_position(selection.tail(), cx)?;
5985 if tail_buffer != cursor_buffer {
5986 return None;
5987 }
5988
5989 let snapshot = cursor_buffer.read(cx).snapshot();
5990 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5991 let prepare_rename = project.update(cx, |project, cx| {
5992 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5993 });
5994
5995 Some(cx.spawn(|this, mut cx| async move {
5996 let rename_range = if let Some(range) = prepare_rename.await? {
5997 Some(range)
5998 } else {
5999 this.read_with(&cx, |this, cx| {
6000 let buffer = this.buffer.read(cx).snapshot(cx);
6001 let mut buffer_highlights = this
6002 .document_highlights_for_position(selection.head(), &buffer)
6003 .filter(|highlight| {
6004 highlight.start.excerpt_id() == selection.head().excerpt_id()
6005 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6006 });
6007 buffer_highlights
6008 .next()
6009 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6010 })?
6011 };
6012 if let Some(rename_range) = rename_range {
6013 let rename_buffer_range = rename_range.to_offset(&snapshot);
6014 let cursor_offset_in_rename_range =
6015 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6016
6017 this.update(&mut cx, |this, cx| {
6018 this.take_rename(false, cx);
6019 let style = this.style(cx);
6020 let buffer = this.buffer.read(cx).read(cx);
6021 let cursor_offset = selection.head().to_offset(&buffer);
6022 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6023 let rename_end = rename_start + rename_buffer_range.len();
6024 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6025 let mut old_highlight_id = None;
6026 let old_name: Arc<str> = buffer
6027 .chunks(rename_start..rename_end, true)
6028 .map(|chunk| {
6029 if old_highlight_id.is_none() {
6030 old_highlight_id = chunk.syntax_highlight_id;
6031 }
6032 chunk.text
6033 })
6034 .collect::<String>()
6035 .into();
6036
6037 drop(buffer);
6038
6039 // Position the selection in the rename editor so that it matches the current selection.
6040 this.show_local_selections = false;
6041 let rename_editor = cx.add_view(|cx| {
6042 let mut editor = Editor::single_line(None, cx);
6043 if let Some(old_highlight_id) = old_highlight_id {
6044 editor.override_text_style =
6045 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6046 }
6047 editor.buffer.update(cx, |buffer, cx| {
6048 buffer.edit([(0..0, old_name.clone())], None, cx)
6049 });
6050 editor.select_all(&SelectAll, cx);
6051 editor
6052 });
6053
6054 let ranges = this
6055 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6056 .into_iter()
6057 .flat_map(|(_, ranges)| ranges)
6058 .chain(
6059 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6060 .into_iter()
6061 .flat_map(|(_, ranges)| ranges),
6062 )
6063 .collect();
6064
6065 this.highlight_text::<Rename>(
6066 ranges,
6067 HighlightStyle {
6068 fade_out: Some(style.rename_fade),
6069 ..Default::default()
6070 },
6071 cx,
6072 );
6073 cx.focus(&rename_editor);
6074 let block_id = this.insert_blocks(
6075 [BlockProperties {
6076 style: BlockStyle::Flex,
6077 position: range.start.clone(),
6078 height: 1,
6079 render: Arc::new({
6080 let editor = rename_editor.clone();
6081 move |cx: &mut BlockContext| {
6082 ChildView::new(&editor, cx)
6083 .contained()
6084 .with_padding_left(cx.anchor_x)
6085 .into_any()
6086 }
6087 }),
6088 disposition: BlockDisposition::Below,
6089 }],
6090 cx,
6091 )[0];
6092 this.pending_rename = Some(RenameState {
6093 range,
6094 old_name,
6095 editor: rename_editor,
6096 block_id,
6097 });
6098 })?;
6099 }
6100
6101 Ok(())
6102 }))
6103 }
6104
6105 pub fn confirm_rename(
6106 workspace: &mut Workspace,
6107 _: &ConfirmRename,
6108 cx: &mut ViewContext<Workspace>,
6109 ) -> Option<Task<Result<()>>> {
6110 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6111
6112 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6113 let rename = editor.take_rename(false, cx)?;
6114 let buffer = editor.buffer.read(cx);
6115 let (start_buffer, start) =
6116 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6117 let (end_buffer, end) =
6118 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6119 if start_buffer == end_buffer {
6120 let new_name = rename.editor.read(cx).text(cx);
6121 Some((start_buffer, start..end, rename.old_name, new_name))
6122 } else {
6123 None
6124 }
6125 })?;
6126
6127 let rename = workspace.project().clone().update(cx, |project, cx| {
6128 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6129 });
6130
6131 let editor = editor.downgrade();
6132 Some(cx.spawn(|workspace, mut cx| async move {
6133 let project_transaction = rename.await?;
6134 Self::open_project_transaction(
6135 &editor,
6136 workspace,
6137 project_transaction,
6138 format!("Rename: {} → {}", old_name, new_name),
6139 cx.clone(),
6140 )
6141 .await?;
6142
6143 editor.update(&mut cx, |editor, cx| {
6144 editor.refresh_document_highlights(cx);
6145 })?;
6146 Ok(())
6147 }))
6148 }
6149
6150 fn take_rename(
6151 &mut self,
6152 moving_cursor: bool,
6153 cx: &mut ViewContext<Self>,
6154 ) -> Option<RenameState> {
6155 let rename = self.pending_rename.take()?;
6156 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
6157 self.clear_text_highlights::<Rename>(cx);
6158 self.show_local_selections = true;
6159
6160 if moving_cursor {
6161 let rename_editor = rename.editor.read(cx);
6162 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6163
6164 // Update the selection to match the position of the selection inside
6165 // the rename editor.
6166 let snapshot = self.buffer.read(cx).read(cx);
6167 let rename_range = rename.range.to_offset(&snapshot);
6168 let cursor_in_editor = snapshot
6169 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6170 .min(rename_range.end);
6171 drop(snapshot);
6172
6173 self.change_selections(None, cx, |s| {
6174 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6175 });
6176 } else {
6177 self.refresh_document_highlights(cx);
6178 }
6179
6180 Some(rename)
6181 }
6182
6183 #[cfg(any(test, feature = "test-support"))]
6184 pub fn pending_rename(&self) -> Option<&RenameState> {
6185 self.pending_rename.as_ref()
6186 }
6187
6188 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6189 let project = match &self.project {
6190 Some(project) => project.clone(),
6191 None => return None,
6192 };
6193
6194 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6195 }
6196
6197 fn perform_format(
6198 &mut self,
6199 project: ModelHandle<Project>,
6200 trigger: FormatTrigger,
6201 cx: &mut ViewContext<Self>,
6202 ) -> Task<Result<()>> {
6203 let buffer = self.buffer().clone();
6204 let buffers = buffer.read(cx).all_buffers();
6205
6206 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6207 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6208
6209 cx.spawn(|_, mut cx| async move {
6210 let transaction = futures::select_biased! {
6211 _ = timeout => {
6212 log::warn!("timed out waiting for formatting");
6213 None
6214 }
6215 transaction = format.log_err().fuse() => transaction,
6216 };
6217
6218 buffer.update(&mut cx, |buffer, cx| {
6219 if let Some(transaction) = transaction {
6220 if !buffer.is_singleton() {
6221 buffer.push_transaction(&transaction.0, cx);
6222 }
6223 }
6224
6225 cx.notify();
6226 });
6227
6228 Ok(())
6229 })
6230 }
6231
6232 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6233 if let Some(project) = self.project.clone() {
6234 self.buffer.update(cx, |multi_buffer, cx| {
6235 project.update(cx, |project, cx| {
6236 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6237 });
6238 })
6239 }
6240 }
6241
6242 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6243 cx.show_character_palette();
6244 }
6245
6246 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6247 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6248 let buffer = self.buffer.read(cx).snapshot(cx);
6249 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6250 let is_valid = buffer
6251 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6252 .any(|entry| {
6253 entry.diagnostic.is_primary
6254 && !entry.range.is_empty()
6255 && entry.range.start == primary_range_start
6256 && entry.diagnostic.message == active_diagnostics.primary_message
6257 });
6258
6259 if is_valid != active_diagnostics.is_valid {
6260 active_diagnostics.is_valid = is_valid;
6261 let mut new_styles = HashMap::default();
6262 for (block_id, diagnostic) in &active_diagnostics.blocks {
6263 new_styles.insert(
6264 *block_id,
6265 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6266 );
6267 }
6268 self.display_map
6269 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6270 }
6271 }
6272 }
6273
6274 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6275 self.dismiss_diagnostics(cx);
6276 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6277 let buffer = self.buffer.read(cx).snapshot(cx);
6278
6279 let mut primary_range = None;
6280 let mut primary_message = None;
6281 let mut group_end = Point::zero();
6282 let diagnostic_group = buffer
6283 .diagnostic_group::<Point>(group_id)
6284 .map(|entry| {
6285 if entry.range.end > group_end {
6286 group_end = entry.range.end;
6287 }
6288 if entry.diagnostic.is_primary {
6289 primary_range = Some(entry.range.clone());
6290 primary_message = Some(entry.diagnostic.message.clone());
6291 }
6292 entry
6293 })
6294 .collect::<Vec<_>>();
6295 let primary_range = primary_range?;
6296 let primary_message = primary_message?;
6297 let primary_range =
6298 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6299
6300 let blocks = display_map
6301 .insert_blocks(
6302 diagnostic_group.iter().map(|entry| {
6303 let diagnostic = entry.diagnostic.clone();
6304 let message_height = diagnostic.message.lines().count() as u8;
6305 BlockProperties {
6306 style: BlockStyle::Fixed,
6307 position: buffer.anchor_after(entry.range.start),
6308 height: message_height,
6309 render: diagnostic_block_renderer(diagnostic, true),
6310 disposition: BlockDisposition::Below,
6311 }
6312 }),
6313 cx,
6314 )
6315 .into_iter()
6316 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6317 .collect();
6318
6319 Some(ActiveDiagnosticGroup {
6320 primary_range,
6321 primary_message,
6322 blocks,
6323 is_valid: true,
6324 })
6325 });
6326 self.active_diagnostics.is_some()
6327 }
6328
6329 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6330 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6331 self.display_map.update(cx, |display_map, cx| {
6332 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6333 });
6334 cx.notify();
6335 }
6336 }
6337
6338 pub fn set_selections_from_remote(
6339 &mut self,
6340 selections: Vec<Selection<Anchor>>,
6341 pending_selection: Option<Selection<Anchor>>,
6342 cx: &mut ViewContext<Self>,
6343 ) {
6344 let old_cursor_position = self.selections.newest_anchor().head();
6345 self.selections.change_with(cx, |s| {
6346 s.select_anchors(selections);
6347 if let Some(pending_selection) = pending_selection {
6348 s.set_pending(pending_selection, SelectMode::Character);
6349 } else {
6350 s.clear_pending();
6351 }
6352 });
6353 self.selections_did_change(false, &old_cursor_position, cx);
6354 }
6355
6356 fn push_to_selection_history(&mut self) {
6357 self.selection_history.push(SelectionHistoryEntry {
6358 selections: self.selections.disjoint_anchors(),
6359 select_next_state: self.select_next_state.clone(),
6360 add_selections_state: self.add_selections_state.clone(),
6361 });
6362 }
6363
6364 pub fn transact(
6365 &mut self,
6366 cx: &mut ViewContext<Self>,
6367 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6368 ) -> Option<TransactionId> {
6369 self.start_transaction_at(Instant::now(), cx);
6370 update(self, cx);
6371 self.end_transaction_at(Instant::now(), cx)
6372 }
6373
6374 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6375 self.end_selection(cx);
6376 if let Some(tx_id) = self
6377 .buffer
6378 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6379 {
6380 self.selection_history
6381 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6382 }
6383 }
6384
6385 fn end_transaction_at(
6386 &mut self,
6387 now: Instant,
6388 cx: &mut ViewContext<Self>,
6389 ) -> Option<TransactionId> {
6390 if let Some(tx_id) = self
6391 .buffer
6392 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6393 {
6394 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6395 *end_selections = Some(self.selections.disjoint_anchors());
6396 } else {
6397 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6398 }
6399
6400 cx.emit(Event::Edited);
6401 Some(tx_id)
6402 } else {
6403 None
6404 }
6405 }
6406
6407 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6408 let mut fold_ranges = Vec::new();
6409
6410 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6411
6412 let selections = self.selections.all::<Point>(cx);
6413 for selection in selections {
6414 let range = selection.range().sorted();
6415 let buffer_start_row = range.start.row;
6416
6417 for row in (0..=range.end.row).rev() {
6418 let fold_range = display_map.foldable_range(row);
6419
6420 if let Some(fold_range) = fold_range {
6421 if fold_range.end.row >= buffer_start_row {
6422 fold_ranges.push(fold_range);
6423 if row <= range.start.row {
6424 break;
6425 }
6426 }
6427 }
6428 }
6429 }
6430
6431 self.fold_ranges(fold_ranges, true, cx);
6432 }
6433
6434 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6435 let buffer_row = fold_at.buffer_row;
6436 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6437
6438 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6439 let autoscroll = self
6440 .selections
6441 .all::<Point>(cx)
6442 .iter()
6443 .any(|selection| fold_range.overlaps(&selection.range()));
6444
6445 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6446 }
6447 }
6448
6449 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6450 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6451 let buffer = &display_map.buffer_snapshot;
6452 let selections = self.selections.all::<Point>(cx);
6453 let ranges = selections
6454 .iter()
6455 .map(|s| {
6456 let range = s.display_range(&display_map).sorted();
6457 let mut start = range.start.to_point(&display_map);
6458 let mut end = range.end.to_point(&display_map);
6459 start.column = 0;
6460 end.column = buffer.line_len(end.row);
6461 start..end
6462 })
6463 .collect::<Vec<_>>();
6464
6465 self.unfold_ranges(ranges, true, true, cx);
6466 }
6467
6468 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6469 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6470
6471 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6472 ..Point::new(
6473 unfold_at.buffer_row,
6474 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6475 );
6476
6477 let autoscroll = self
6478 .selections
6479 .all::<Point>(cx)
6480 .iter()
6481 .any(|selection| selection.range().overlaps(&intersection_range));
6482
6483 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6484 }
6485
6486 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6487 let selections = self.selections.all::<Point>(cx);
6488 let ranges = selections.into_iter().map(|s| s.start..s.end);
6489 self.fold_ranges(ranges, true, cx);
6490 }
6491
6492 pub fn fold_ranges<T: ToOffset + Clone>(
6493 &mut self,
6494 ranges: impl IntoIterator<Item = Range<T>>,
6495 auto_scroll: bool,
6496 cx: &mut ViewContext<Self>,
6497 ) {
6498 let mut ranges = ranges.into_iter().peekable();
6499 if ranges.peek().is_some() {
6500 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6501
6502 if auto_scroll {
6503 self.request_autoscroll(Autoscroll::fit(), cx);
6504 }
6505
6506 cx.notify();
6507 }
6508 }
6509
6510 pub fn unfold_ranges<T: ToOffset + Clone>(
6511 &mut self,
6512 ranges: impl IntoIterator<Item = Range<T>>,
6513 inclusive: bool,
6514 auto_scroll: bool,
6515 cx: &mut ViewContext<Self>,
6516 ) {
6517 let mut ranges = ranges.into_iter().peekable();
6518 if ranges.peek().is_some() {
6519 self.display_map
6520 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6521 if auto_scroll {
6522 self.request_autoscroll(Autoscroll::fit(), cx);
6523 }
6524
6525 cx.notify();
6526 }
6527 }
6528
6529 pub fn gutter_hover(
6530 &mut self,
6531 GutterHover { hovered }: &GutterHover,
6532 cx: &mut ViewContext<Self>,
6533 ) {
6534 self.gutter_hovered = *hovered;
6535 cx.notify();
6536 }
6537
6538 pub fn insert_blocks(
6539 &mut self,
6540 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6541 cx: &mut ViewContext<Self>,
6542 ) -> Vec<BlockId> {
6543 let blocks = self
6544 .display_map
6545 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6546 self.request_autoscroll(Autoscroll::fit(), cx);
6547 blocks
6548 }
6549
6550 pub fn replace_blocks(
6551 &mut self,
6552 blocks: HashMap<BlockId, RenderBlock>,
6553 cx: &mut ViewContext<Self>,
6554 ) {
6555 self.display_map
6556 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6557 self.request_autoscroll(Autoscroll::fit(), cx);
6558 }
6559
6560 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6561 self.display_map.update(cx, |display_map, cx| {
6562 display_map.remove_blocks(block_ids, cx)
6563 });
6564 }
6565
6566 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6567 self.display_map
6568 .update(cx, |map, cx| map.snapshot(cx))
6569 .longest_row()
6570 }
6571
6572 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6573 self.display_map
6574 .update(cx, |map, cx| map.snapshot(cx))
6575 .max_point()
6576 }
6577
6578 pub fn text(&self, cx: &AppContext) -> String {
6579 self.buffer.read(cx).read(cx).text()
6580 }
6581
6582 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6583 self.transact(cx, |this, cx| {
6584 this.buffer
6585 .read(cx)
6586 .as_singleton()
6587 .expect("you can only call set_text on editors for singleton buffers")
6588 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6589 });
6590 }
6591
6592 pub fn display_text(&self, cx: &mut AppContext) -> String {
6593 self.display_map
6594 .update(cx, |map, cx| map.snapshot(cx))
6595 .text()
6596 }
6597
6598 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6599 let settings = self.buffer.read(cx).settings_at(0, cx);
6600 let mode = self
6601 .soft_wrap_mode_override
6602 .unwrap_or_else(|| settings.soft_wrap);
6603 match mode {
6604 language_settings::SoftWrap::None => SoftWrap::None,
6605 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6606 language_settings::SoftWrap::PreferredLineLength => {
6607 SoftWrap::Column(settings.preferred_line_length)
6608 }
6609 }
6610 }
6611
6612 pub fn set_soft_wrap_mode(
6613 &mut self,
6614 mode: language_settings::SoftWrap,
6615 cx: &mut ViewContext<Self>,
6616 ) {
6617 self.soft_wrap_mode_override = Some(mode);
6618 cx.notify();
6619 }
6620
6621 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6622 self.display_map
6623 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6624 }
6625
6626 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6627 if self.soft_wrap_mode_override.is_some() {
6628 self.soft_wrap_mode_override.take();
6629 } else {
6630 let soft_wrap = match self.soft_wrap_mode(cx) {
6631 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
6632 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
6633 };
6634 self.soft_wrap_mode_override = Some(soft_wrap);
6635 }
6636 cx.notify();
6637 }
6638
6639 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6640 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6641 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6642 cx.reveal_path(&file.abs_path(cx));
6643 }
6644 }
6645 }
6646
6647 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6648 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6649 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6650 if let Some(path) = file.abs_path(cx).to_str() {
6651 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6652 }
6653 }
6654 }
6655 }
6656
6657 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6658 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6659 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6660 if let Some(path) = file.path().to_str() {
6661 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6662 }
6663 }
6664 }
6665 }
6666
6667 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6668 self.highlighted_rows = rows;
6669 }
6670
6671 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6672 self.highlighted_rows.clone()
6673 }
6674
6675 pub fn highlight_background<T: 'static>(
6676 &mut self,
6677 ranges: Vec<Range<Anchor>>,
6678 color_fetcher: fn(&Theme) -> Color,
6679 cx: &mut ViewContext<Self>,
6680 ) {
6681 self.background_highlights
6682 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6683 cx.notify();
6684 }
6685
6686 #[allow(clippy::type_complexity)]
6687 pub fn clear_background_highlights<T: 'static>(
6688 &mut self,
6689 cx: &mut ViewContext<Self>,
6690 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6691 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6692 if highlights.is_some() {
6693 cx.notify();
6694 }
6695 highlights
6696 }
6697
6698 #[cfg(feature = "test-support")]
6699 pub fn all_background_highlights(
6700 &mut self,
6701 cx: &mut ViewContext<Self>,
6702 ) -> Vec<(Range<DisplayPoint>, Color)> {
6703 let snapshot = self.snapshot(cx);
6704 let buffer = &snapshot.buffer_snapshot;
6705 let start = buffer.anchor_before(0);
6706 let end = buffer.anchor_after(buffer.len());
6707 let theme = theme::current(cx);
6708 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
6709 }
6710
6711 fn document_highlights_for_position<'a>(
6712 &'a self,
6713 position: Anchor,
6714 buffer: &'a MultiBufferSnapshot,
6715 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6716 let read_highlights = self
6717 .background_highlights
6718 .get(&TypeId::of::<DocumentHighlightRead>())
6719 .map(|h| &h.1);
6720 let write_highlights = self
6721 .background_highlights
6722 .get(&TypeId::of::<DocumentHighlightWrite>())
6723 .map(|h| &h.1);
6724 let left_position = position.bias_left(buffer);
6725 let right_position = position.bias_right(buffer);
6726 read_highlights
6727 .into_iter()
6728 .chain(write_highlights)
6729 .flat_map(move |ranges| {
6730 let start_ix = match ranges.binary_search_by(|probe| {
6731 let cmp = probe.end.cmp(&left_position, buffer);
6732 if cmp.is_ge() {
6733 Ordering::Greater
6734 } else {
6735 Ordering::Less
6736 }
6737 }) {
6738 Ok(i) | Err(i) => i,
6739 };
6740
6741 let right_position = right_position.clone();
6742 ranges[start_ix..]
6743 .iter()
6744 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6745 })
6746 }
6747
6748 pub fn background_highlights_in_range(
6749 &self,
6750 search_range: Range<Anchor>,
6751 display_snapshot: &DisplaySnapshot,
6752 theme: &Theme,
6753 ) -> Vec<(Range<DisplayPoint>, Color)> {
6754 let mut results = Vec::new();
6755 let buffer = &display_snapshot.buffer_snapshot;
6756 for (color_fetcher, ranges) in self.background_highlights.values() {
6757 let color = color_fetcher(theme);
6758 let start_ix = match ranges.binary_search_by(|probe| {
6759 let cmp = probe.end.cmp(&search_range.start, buffer);
6760 if cmp.is_gt() {
6761 Ordering::Greater
6762 } else {
6763 Ordering::Less
6764 }
6765 }) {
6766 Ok(i) | Err(i) => i,
6767 };
6768 for range in &ranges[start_ix..] {
6769 if range.start.cmp(&search_range.end, buffer).is_ge() {
6770 break;
6771 }
6772 let start = range
6773 .start
6774 .to_point(buffer)
6775 .to_display_point(display_snapshot);
6776 let end = range
6777 .end
6778 .to_point(buffer)
6779 .to_display_point(display_snapshot);
6780 results.push((start..end, color))
6781 }
6782 }
6783 results
6784 }
6785
6786 pub fn highlight_text<T: 'static>(
6787 &mut self,
6788 ranges: Vec<Range<Anchor>>,
6789 style: HighlightStyle,
6790 cx: &mut ViewContext<Self>,
6791 ) {
6792 self.display_map.update(cx, |map, _| {
6793 map.highlight_text(TypeId::of::<T>(), ranges, style)
6794 });
6795 cx.notify();
6796 }
6797
6798 pub fn text_highlights<'a, T: 'static>(
6799 &'a self,
6800 cx: &'a AppContext,
6801 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6802 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6803 }
6804
6805 pub fn clear_text_highlights<T: 'static>(
6806 &mut self,
6807 cx: &mut ViewContext<Self>,
6808 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6809 let highlights = self
6810 .display_map
6811 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6812 if highlights.is_some() {
6813 cx.notify();
6814 }
6815 highlights
6816 }
6817
6818 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6819 self.blink_manager.read(cx).visible() && self.focused
6820 }
6821
6822 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6823 cx.notify();
6824 }
6825
6826 fn on_buffer_event(
6827 &mut self,
6828 _: ModelHandle<MultiBuffer>,
6829 event: &multi_buffer::Event,
6830 cx: &mut ViewContext<Self>,
6831 ) {
6832 match event {
6833 multi_buffer::Event::Edited => {
6834 self.refresh_active_diagnostics(cx);
6835 self.refresh_code_actions(cx);
6836 if self.has_active_copilot_suggestion(cx) {
6837 self.update_visible_copilot_suggestion(cx);
6838 }
6839 cx.emit(Event::BufferEdited);
6840 }
6841 multi_buffer::Event::ExcerptsAdded {
6842 buffer,
6843 predecessor,
6844 excerpts,
6845 } => cx.emit(Event::ExcerptsAdded {
6846 buffer: buffer.clone(),
6847 predecessor: *predecessor,
6848 excerpts: excerpts.clone(),
6849 }),
6850 multi_buffer::Event::ExcerptsRemoved { ids } => {
6851 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6852 }
6853 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6854 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6855 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6856 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6857 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6858 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6859 multi_buffer::Event::DiagnosticsUpdated => {
6860 self.refresh_active_diagnostics(cx);
6861 }
6862 multi_buffer::Event::LanguageChanged => {}
6863 }
6864 }
6865
6866 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6867 cx.notify();
6868 }
6869
6870 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
6871 self.refresh_copilot_suggestions(true, cx);
6872 }
6873
6874 pub fn set_searchable(&mut self, searchable: bool) {
6875 self.searchable = searchable;
6876 }
6877
6878 pub fn searchable(&self) -> bool {
6879 self.searchable
6880 }
6881
6882 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6883 let active_item = workspace.active_item(cx);
6884 let editor_handle = if let Some(editor) = active_item
6885 .as_ref()
6886 .and_then(|item| item.act_as::<Self>(cx))
6887 {
6888 editor
6889 } else {
6890 cx.propagate_action();
6891 return;
6892 };
6893
6894 let editor = editor_handle.read(cx);
6895 let buffer = editor.buffer.read(cx);
6896 if buffer.is_singleton() {
6897 cx.propagate_action();
6898 return;
6899 }
6900
6901 let mut new_selections_by_buffer = HashMap::default();
6902 for selection in editor.selections.all::<usize>(cx) {
6903 for (buffer, mut range) in
6904 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6905 {
6906 if selection.reversed {
6907 mem::swap(&mut range.start, &mut range.end);
6908 }
6909 new_selections_by_buffer
6910 .entry(buffer)
6911 .or_insert(Vec::new())
6912 .push(range)
6913 }
6914 }
6915
6916 editor_handle.update(cx, |editor, cx| {
6917 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6918 });
6919 let pane = workspace.active_pane().clone();
6920 pane.update(cx, |pane, _| pane.disable_history());
6921
6922 // We defer the pane interaction because we ourselves are a workspace item
6923 // and activating a new item causes the pane to call a method on us reentrantly,
6924 // which panics if we're on the stack.
6925 cx.defer(move |workspace, cx| {
6926 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6927 let editor = workspace.open_project_item::<Self>(buffer, cx);
6928 editor.update(cx, |editor, cx| {
6929 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6930 s.select_ranges(ranges);
6931 });
6932 });
6933 }
6934
6935 pane.update(cx, |pane, _| pane.enable_history());
6936 });
6937 }
6938
6939 fn jump(
6940 workspace: &mut Workspace,
6941 path: ProjectPath,
6942 position: Point,
6943 anchor: language::Anchor,
6944 cx: &mut ViewContext<Workspace>,
6945 ) {
6946 let editor = workspace.open_path(path, None, true, cx);
6947 cx.spawn(|_, mut cx| async move {
6948 let editor = editor
6949 .await?
6950 .downcast::<Editor>()
6951 .ok_or_else(|| anyhow!("opened item was not an editor"))?
6952 .downgrade();
6953 editor.update(&mut cx, |editor, cx| {
6954 let buffer = editor
6955 .buffer()
6956 .read(cx)
6957 .as_singleton()
6958 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
6959 let buffer = buffer.read(cx);
6960 let cursor = if buffer.can_resolve(&anchor) {
6961 language::ToPoint::to_point(&anchor, buffer)
6962 } else {
6963 buffer.clip_point(position, Bias::Left)
6964 };
6965
6966 let nav_history = editor.nav_history.take();
6967 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6968 s.select_ranges([cursor..cursor]);
6969 });
6970 editor.nav_history = nav_history;
6971
6972 anyhow::Ok(())
6973 })??;
6974
6975 anyhow::Ok(())
6976 })
6977 .detach_and_log_err(cx);
6978 }
6979
6980 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6981 let snapshot = self.buffer.read(cx).read(cx);
6982 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6983 Some(
6984 ranges
6985 .iter()
6986 .map(move |range| {
6987 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6988 })
6989 .collect(),
6990 )
6991 }
6992
6993 fn selection_replacement_ranges(
6994 &self,
6995 range: Range<OffsetUtf16>,
6996 cx: &AppContext,
6997 ) -> Vec<Range<OffsetUtf16>> {
6998 let selections = self.selections.all::<OffsetUtf16>(cx);
6999 let newest_selection = selections
7000 .iter()
7001 .max_by_key(|selection| selection.id)
7002 .unwrap();
7003 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7004 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7005 let snapshot = self.buffer.read(cx).read(cx);
7006 selections
7007 .into_iter()
7008 .map(|mut selection| {
7009 selection.start.0 =
7010 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7011 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7012 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7013 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7014 })
7015 .collect()
7016 }
7017
7018 fn report_copilot_event(
7019 &self,
7020 suggestion_id: Option<String>,
7021 suggestion_accepted: bool,
7022 cx: &AppContext,
7023 ) {
7024 let Some(project) = &self.project else {
7025 return
7026 };
7027
7028 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7029 let file_extension = self
7030 .buffer
7031 .read(cx)
7032 .as_singleton()
7033 .and_then(|b| b.read(cx).file())
7034 .and_then(|file| Path::new(file.file_name(cx)).extension())
7035 .and_then(|e| e.to_str())
7036 .map(|a| a.to_string());
7037
7038 let telemetry = project.read(cx).client().telemetry().clone();
7039 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7040
7041 let event = ClickhouseEvent::Copilot {
7042 suggestion_id,
7043 suggestion_accepted,
7044 file_extension,
7045 };
7046 telemetry.report_clickhouse_event(event, telemetry_settings);
7047 }
7048
7049 fn report_editor_event(
7050 &self,
7051 name: &'static str,
7052 file_extension: Option<String>,
7053 cx: &AppContext,
7054 ) {
7055 let Some(project) = &self.project else {
7056 return
7057 };
7058
7059 // If None, we are in a file without an extension
7060 let file_extension = file_extension.or(self
7061 .buffer
7062 .read(cx)
7063 .as_singleton()
7064 .and_then(|b| b.read(cx).file())
7065 .and_then(|file| Path::new(file.file_name(cx)).extension())
7066 .and_then(|e| e.to_str())
7067 .map(|a| a.to_string()));
7068
7069 let vim_mode = cx
7070 .global::<SettingsStore>()
7071 .untyped_user_settings()
7072 .get("vim_mode")
7073 == Some(&serde_json::Value::Bool(true));
7074 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7075 let copilot_enabled = all_language_settings(cx).copilot_enabled(None, None);
7076 let copilot_enabled_for_language = self
7077 .buffer
7078 .read(cx)
7079 .settings_at(0, cx)
7080 .show_copilot_suggestions;
7081
7082 let telemetry = project.read(cx).client().telemetry().clone();
7083 telemetry.report_mixpanel_event(
7084 match name {
7085 "open" => "open editor",
7086 "save" => "save editor",
7087 _ => name,
7088 },
7089 json!({ "File Extension": file_extension, "Vim Mode": vim_mode, "In Clickhouse": true }),
7090 telemetry_settings,
7091 );
7092 let event = ClickhouseEvent::Editor {
7093 file_extension,
7094 vim_mode,
7095 operation: name,
7096 copilot_enabled,
7097 copilot_enabled_for_language,
7098 };
7099 telemetry.report_clickhouse_event(event, telemetry_settings)
7100 }
7101
7102 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7103 /// with each line being an array of {text, highlight} objects.
7104 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7105 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7106 return;
7107 };
7108
7109 #[derive(Serialize)]
7110 struct Chunk<'a> {
7111 text: String,
7112 highlight: Option<&'a str>,
7113 }
7114
7115 let snapshot = buffer.read(cx).snapshot();
7116 let range = self
7117 .selected_text_range(cx)
7118 .and_then(|selected_range| {
7119 if selected_range.is_empty() {
7120 None
7121 } else {
7122 Some(selected_range)
7123 }
7124 })
7125 .unwrap_or_else(|| 0..snapshot.len());
7126
7127 let chunks = snapshot.chunks(range, true);
7128 let mut lines = Vec::new();
7129 let mut line: VecDeque<Chunk> = VecDeque::new();
7130
7131 let theme = &theme::current(cx).editor.syntax;
7132
7133 for chunk in chunks {
7134 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7135 let mut chunk_lines = chunk.text.split("\n").peekable();
7136 while let Some(text) = chunk_lines.next() {
7137 let mut merged_with_last_token = false;
7138 if let Some(last_token) = line.back_mut() {
7139 if last_token.highlight == highlight {
7140 last_token.text.push_str(text);
7141 merged_with_last_token = true;
7142 }
7143 }
7144
7145 if !merged_with_last_token {
7146 line.push_back(Chunk {
7147 text: text.into(),
7148 highlight,
7149 });
7150 }
7151
7152 if chunk_lines.peek().is_some() {
7153 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7154 line.pop_front();
7155 }
7156 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7157 line.pop_back();
7158 }
7159
7160 lines.push(mem::take(&mut line));
7161 }
7162 }
7163 }
7164
7165 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7166 cx.write_to_clipboard(ClipboardItem::new(lines));
7167 }
7168}
7169
7170fn consume_contiguous_rows(
7171 contiguous_row_selections: &mut Vec<Selection<Point>>,
7172 selection: &Selection<Point>,
7173 display_map: &DisplaySnapshot,
7174 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7175) -> (u32, u32) {
7176 contiguous_row_selections.push(selection.clone());
7177 let start_row = selection.start.row;
7178 let mut end_row = ending_row(selection, display_map);
7179
7180 while let Some(next_selection) = selections.peek() {
7181 if next_selection.start.row <= end_row {
7182 end_row = ending_row(next_selection, display_map);
7183 contiguous_row_selections.push(selections.next().unwrap().clone());
7184 } else {
7185 break;
7186 }
7187 }
7188 (start_row, end_row)
7189}
7190
7191fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7192 if next_selection.end.column > 0 || next_selection.is_empty() {
7193 display_map.next_line_boundary(next_selection.end).0.row + 1
7194 } else {
7195 next_selection.end.row
7196 }
7197}
7198
7199impl EditorSnapshot {
7200 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7201 self.display_snapshot.buffer_snapshot.language_at(position)
7202 }
7203
7204 pub fn is_focused(&self) -> bool {
7205 self.is_focused
7206 }
7207
7208 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7209 self.placeholder_text.as_ref()
7210 }
7211
7212 pub fn scroll_position(&self) -> Vector2F {
7213 self.scroll_anchor.scroll_position(&self.display_snapshot)
7214 }
7215}
7216
7217impl Deref for EditorSnapshot {
7218 type Target = DisplaySnapshot;
7219
7220 fn deref(&self) -> &Self::Target {
7221 &self.display_snapshot
7222 }
7223}
7224
7225#[derive(Clone, Debug, PartialEq, Eq)]
7226pub enum Event {
7227 InputIgnored {
7228 text: Arc<str>,
7229 },
7230 ExcerptsAdded {
7231 buffer: ModelHandle<Buffer>,
7232 predecessor: ExcerptId,
7233 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7234 },
7235 ExcerptsRemoved {
7236 ids: Vec<ExcerptId>,
7237 },
7238 BufferEdited,
7239 Edited,
7240 Reparsed,
7241 Focused,
7242 Blurred,
7243 DirtyChanged,
7244 Saved,
7245 TitleChanged,
7246 SelectionsChanged {
7247 local: bool,
7248 },
7249 ScrollPositionChanged {
7250 local: bool,
7251 },
7252 Closed,
7253}
7254
7255pub struct EditorFocused(pub ViewHandle<Editor>);
7256pub struct EditorBlurred(pub ViewHandle<Editor>);
7257pub struct EditorReleased(pub WeakViewHandle<Editor>);
7258
7259impl Entity for Editor {
7260 type Event = Event;
7261
7262 fn release(&mut self, cx: &mut AppContext) {
7263 cx.emit_global(EditorReleased(self.handle.clone()));
7264 }
7265}
7266
7267impl View for Editor {
7268 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7269 let style = self.style(cx);
7270 let font_changed = self.display_map.update(cx, |map, cx| {
7271 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7272 map.set_font(style.text.font_id, style.text.font_size, cx)
7273 });
7274
7275 if font_changed {
7276 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7277 hide_hover(editor, cx);
7278 hide_link_definition(editor, cx);
7279 });
7280 }
7281
7282 Stack::new()
7283 .with_child(EditorElement::new(style.clone()))
7284 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7285 .into_any()
7286 }
7287
7288 fn ui_name() -> &'static str {
7289 "Editor"
7290 }
7291
7292 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7293 if cx.is_self_focused() {
7294 let focused_event = EditorFocused(cx.handle());
7295 cx.emit(Event::Focused);
7296 cx.emit_global(focused_event);
7297 }
7298 if let Some(rename) = self.pending_rename.as_ref() {
7299 cx.focus(&rename.editor);
7300 } else {
7301 if !self.focused {
7302 self.blink_manager.update(cx, BlinkManager::enable);
7303 }
7304 self.focused = true;
7305 self.buffer.update(cx, |buffer, cx| {
7306 buffer.finalize_last_transaction(cx);
7307 if self.leader_replica_id.is_none() {
7308 buffer.set_active_selections(
7309 &self.selections.disjoint_anchors(),
7310 self.selections.line_mode,
7311 self.cursor_shape,
7312 cx,
7313 );
7314 }
7315 });
7316 }
7317 }
7318
7319 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7320 let blurred_event = EditorBlurred(cx.handle());
7321 cx.emit_global(blurred_event);
7322 self.focused = false;
7323 self.blink_manager.update(cx, BlinkManager::disable);
7324 self.buffer
7325 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7326 self.hide_context_menu(cx);
7327 hide_hover(self, cx);
7328 cx.emit(Event::Blurred);
7329 cx.notify();
7330 }
7331
7332 fn modifiers_changed(
7333 &mut self,
7334 event: &gpui::platform::ModifiersChangedEvent,
7335 cx: &mut ViewContext<Self>,
7336 ) -> bool {
7337 let pending_selection = self.has_pending_selection();
7338
7339 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7340 if event.cmd && !pending_selection {
7341 let snapshot = self.snapshot(cx);
7342 let kind = if event.shift {
7343 LinkDefinitionKind::Type
7344 } else {
7345 LinkDefinitionKind::Symbol
7346 };
7347
7348 show_link_definition(kind, self, point, snapshot, cx);
7349 return false;
7350 }
7351 }
7352
7353 {
7354 if self.link_go_to_definition_state.symbol_range.is_some()
7355 || !self.link_go_to_definition_state.definitions.is_empty()
7356 {
7357 self.link_go_to_definition_state.symbol_range.take();
7358 self.link_go_to_definition_state.definitions.clear();
7359 cx.notify();
7360 }
7361
7362 self.link_go_to_definition_state.task = None;
7363
7364 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7365 }
7366
7367 false
7368 }
7369
7370 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7371 Self::reset_to_default_keymap_context(keymap);
7372 let mode = match self.mode {
7373 EditorMode::SingleLine => "single_line",
7374 EditorMode::AutoHeight { .. } => "auto_height",
7375 EditorMode::Full => "full",
7376 };
7377 keymap.add_key("mode", mode);
7378 if self.pending_rename.is_some() {
7379 keymap.add_identifier("renaming");
7380 }
7381 match self.context_menu.as_ref() {
7382 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7383 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7384 None => {}
7385 }
7386 for layer in self.keymap_context_layers.values() {
7387 keymap.extend(layer);
7388 }
7389
7390 if let Some(extension) = self
7391 .buffer
7392 .read(cx)
7393 .as_singleton()
7394 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7395 {
7396 keymap.add_key("extension", extension.to_string());
7397 }
7398 }
7399
7400 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7401 Some(
7402 self.buffer
7403 .read(cx)
7404 .read(cx)
7405 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7406 .collect(),
7407 )
7408 }
7409
7410 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7411 // Prevent the IME menu from appearing when holding down an alphabetic key
7412 // while input is disabled.
7413 if !self.input_enabled {
7414 return None;
7415 }
7416
7417 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7418 Some(range.start.0..range.end.0)
7419 }
7420
7421 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7422 let snapshot = self.buffer.read(cx).read(cx);
7423 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7424 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7425 }
7426
7427 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7428 self.clear_text_highlights::<InputComposition>(cx);
7429 self.ime_transaction.take();
7430 }
7431
7432 fn replace_text_in_range(
7433 &mut self,
7434 range_utf16: Option<Range<usize>>,
7435 text: &str,
7436 cx: &mut ViewContext<Self>,
7437 ) {
7438 self.transact(cx, |this, cx| {
7439 if this.input_enabled {
7440 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7441 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7442 Some(this.selection_replacement_ranges(range_utf16, cx))
7443 } else {
7444 this.marked_text_ranges(cx)
7445 };
7446
7447 if let Some(new_selected_ranges) = new_selected_ranges {
7448 this.change_selections(None, cx, |selections| {
7449 selections.select_ranges(new_selected_ranges)
7450 });
7451 }
7452 }
7453
7454 this.handle_input(text, cx);
7455 });
7456
7457 if !self.input_enabled {
7458 return;
7459 }
7460
7461 if let Some(transaction) = self.ime_transaction {
7462 self.buffer.update(cx, |buffer, cx| {
7463 buffer.group_until_transaction(transaction, cx);
7464 });
7465 }
7466
7467 self.unmark_text(cx);
7468 }
7469
7470 fn replace_and_mark_text_in_range(
7471 &mut self,
7472 range_utf16: Option<Range<usize>>,
7473 text: &str,
7474 new_selected_range_utf16: Option<Range<usize>>,
7475 cx: &mut ViewContext<Self>,
7476 ) {
7477 if !self.input_enabled {
7478 return;
7479 }
7480
7481 let transaction = self.transact(cx, |this, cx| {
7482 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7483 let snapshot = this.buffer.read(cx).read(cx);
7484 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7485 for marked_range in &mut marked_ranges {
7486 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7487 marked_range.start.0 += relative_range_utf16.start;
7488 marked_range.start =
7489 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7490 marked_range.end =
7491 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7492 }
7493 }
7494 Some(marked_ranges)
7495 } else if let Some(range_utf16) = range_utf16 {
7496 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7497 Some(this.selection_replacement_ranges(range_utf16, cx))
7498 } else {
7499 None
7500 };
7501
7502 if let Some(ranges) = ranges_to_replace {
7503 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7504 }
7505
7506 let marked_ranges = {
7507 let snapshot = this.buffer.read(cx).read(cx);
7508 this.selections
7509 .disjoint_anchors()
7510 .iter()
7511 .map(|selection| {
7512 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7513 })
7514 .collect::<Vec<_>>()
7515 };
7516
7517 if text.is_empty() {
7518 this.unmark_text(cx);
7519 } else {
7520 this.highlight_text::<InputComposition>(
7521 marked_ranges.clone(),
7522 this.style(cx).composition_mark,
7523 cx,
7524 );
7525 }
7526
7527 this.handle_input(text, cx);
7528
7529 if let Some(new_selected_range) = new_selected_range_utf16 {
7530 let snapshot = this.buffer.read(cx).read(cx);
7531 let new_selected_ranges = marked_ranges
7532 .into_iter()
7533 .map(|marked_range| {
7534 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7535 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7536 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7537 snapshot.clip_offset_utf16(new_start, Bias::Left)
7538 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7539 })
7540 .collect::<Vec<_>>();
7541
7542 drop(snapshot);
7543 this.change_selections(None, cx, |selections| {
7544 selections.select_ranges(new_selected_ranges)
7545 });
7546 }
7547 });
7548
7549 self.ime_transaction = self.ime_transaction.or(transaction);
7550 if let Some(transaction) = self.ime_transaction {
7551 self.buffer.update(cx, |buffer, cx| {
7552 buffer.group_until_transaction(transaction, cx);
7553 });
7554 }
7555
7556 if self.text_highlights::<InputComposition>(cx).is_none() {
7557 self.ime_transaction.take();
7558 }
7559 }
7560}
7561
7562fn build_style(
7563 settings: &ThemeSettings,
7564 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7565 override_text_style: Option<&OverrideTextStyle>,
7566 cx: &AppContext,
7567) -> EditorStyle {
7568 let font_cache = cx.font_cache();
7569
7570 let theme_id = settings.theme.meta.id;
7571 let mut theme = settings.theme.editor.clone();
7572 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7573 let field_editor_theme = get_field_editor_theme(&settings.theme);
7574 theme.text_color = field_editor_theme.text.color;
7575 theme.selection = field_editor_theme.selection;
7576 theme.background = field_editor_theme
7577 .container
7578 .background_color
7579 .unwrap_or_default();
7580 EditorStyle {
7581 text: field_editor_theme.text,
7582 placeholder_text: field_editor_theme.placeholder_text,
7583 theme,
7584 theme_id,
7585 }
7586 } else {
7587 let font_family_id = settings.buffer_font_family;
7588 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7589 let font_properties = Default::default();
7590 let font_id = font_cache
7591 .select_font(font_family_id, &font_properties)
7592 .unwrap();
7593 let font_size = settings.buffer_font_size(cx);
7594 EditorStyle {
7595 text: TextStyle {
7596 color: settings.theme.editor.text_color,
7597 font_family_name,
7598 font_family_id,
7599 font_id,
7600 font_size,
7601 font_properties,
7602 underline: Default::default(),
7603 },
7604 placeholder_text: None,
7605 theme,
7606 theme_id,
7607 }
7608 };
7609
7610 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7611 if let Some(highlighted) = style
7612 .text
7613 .clone()
7614 .highlight(highlight_style, font_cache)
7615 .log_err()
7616 {
7617 style.text = highlighted;
7618 }
7619 }
7620
7621 style
7622}
7623
7624trait SelectionExt {
7625 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7626 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7627 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7628 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7629 -> Range<u32>;
7630}
7631
7632impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7633 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7634 let start = self.start.to_point(buffer);
7635 let end = self.end.to_point(buffer);
7636 if self.reversed {
7637 end..start
7638 } else {
7639 start..end
7640 }
7641 }
7642
7643 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7644 let start = self.start.to_offset(buffer);
7645 let end = self.end.to_offset(buffer);
7646 if self.reversed {
7647 end..start
7648 } else {
7649 start..end
7650 }
7651 }
7652
7653 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7654 let start = self
7655 .start
7656 .to_point(&map.buffer_snapshot)
7657 .to_display_point(map);
7658 let end = self
7659 .end
7660 .to_point(&map.buffer_snapshot)
7661 .to_display_point(map);
7662 if self.reversed {
7663 end..start
7664 } else {
7665 start..end
7666 }
7667 }
7668
7669 fn spanned_rows(
7670 &self,
7671 include_end_if_at_line_start: bool,
7672 map: &DisplaySnapshot,
7673 ) -> Range<u32> {
7674 let start = self.start.to_point(&map.buffer_snapshot);
7675 let mut end = self.end.to_point(&map.buffer_snapshot);
7676 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7677 end.row -= 1;
7678 }
7679
7680 let buffer_start = map.prev_line_boundary(start).0;
7681 let buffer_end = map.next_line_boundary(end).0;
7682 buffer_start.row..buffer_end.row + 1
7683 }
7684}
7685
7686impl<T: InvalidationRegion> InvalidationStack<T> {
7687 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7688 where
7689 S: Clone + ToOffset,
7690 {
7691 while let Some(region) = self.last() {
7692 let all_selections_inside_invalidation_ranges =
7693 if selections.len() == region.ranges().len() {
7694 selections
7695 .iter()
7696 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7697 .all(|(selection, invalidation_range)| {
7698 let head = selection.head().to_offset(buffer);
7699 invalidation_range.start <= head && invalidation_range.end >= head
7700 })
7701 } else {
7702 false
7703 };
7704
7705 if all_selections_inside_invalidation_ranges {
7706 break;
7707 } else {
7708 self.pop();
7709 }
7710 }
7711 }
7712}
7713
7714impl<T> Default for InvalidationStack<T> {
7715 fn default() -> Self {
7716 Self(Default::default())
7717 }
7718}
7719
7720impl<T> Deref for InvalidationStack<T> {
7721 type Target = Vec<T>;
7722
7723 fn deref(&self) -> &Self::Target {
7724 &self.0
7725 }
7726}
7727
7728impl<T> DerefMut for InvalidationStack<T> {
7729 fn deref_mut(&mut self) -> &mut Self::Target {
7730 &mut self.0
7731 }
7732}
7733
7734impl InvalidationRegion for SnippetState {
7735 fn ranges(&self) -> &[Range<Anchor>] {
7736 &self.ranges[self.active_index]
7737 }
7738}
7739
7740impl Deref for EditorStyle {
7741 type Target = theme::Editor;
7742
7743 fn deref(&self) -> &Self::Target {
7744 &self.theme
7745 }
7746}
7747
7748pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7749 let mut highlighted_lines = Vec::new();
7750 for (index, line) in diagnostic.message.lines().enumerate() {
7751 let line = match &diagnostic.source {
7752 Some(source) if index == 0 => {
7753 let source_highlight = Vec::from_iter(0..source.len());
7754 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7755 }
7756
7757 _ => highlight_diagnostic_message(Vec::new(), line),
7758 };
7759 highlighted_lines.push(line);
7760 }
7761
7762 Arc::new(move |cx: &mut BlockContext| {
7763 let settings = settings::get::<ThemeSettings>(cx);
7764 let theme = &settings.theme.editor;
7765 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7766 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
7767 Flex::column()
7768 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7769 Label::new(
7770 line.clone(),
7771 style.message.clone().with_font_size(font_size),
7772 )
7773 .with_highlights(highlights.clone())
7774 .contained()
7775 .with_margin_left(cx.anchor_x)
7776 }))
7777 .aligned()
7778 .left()
7779 .into_any()
7780 })
7781}
7782
7783pub fn highlight_diagnostic_message(
7784 inital_highlights: Vec<usize>,
7785 message: &str,
7786) -> (String, Vec<usize>) {
7787 let mut message_without_backticks = String::new();
7788 let mut prev_offset = 0;
7789 let mut inside_block = false;
7790 let mut highlights = inital_highlights;
7791 for (match_ix, (offset, _)) in message
7792 .match_indices('`')
7793 .chain([(message.len(), "")])
7794 .enumerate()
7795 {
7796 message_without_backticks.push_str(&message[prev_offset..offset]);
7797 if inside_block {
7798 highlights.extend(prev_offset - match_ix..offset - match_ix);
7799 }
7800
7801 inside_block = !inside_block;
7802 prev_offset = offset + 1;
7803 }
7804
7805 (message_without_backticks, highlights)
7806}
7807
7808pub fn diagnostic_style(
7809 severity: DiagnosticSeverity,
7810 valid: bool,
7811 theme: &theme::Editor,
7812) -> DiagnosticStyle {
7813 match (severity, valid) {
7814 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
7815 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
7816 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
7817 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
7818 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
7819 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
7820 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
7821 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
7822 _ => theme.invalid_hint_diagnostic.clone(),
7823 }
7824}
7825
7826pub fn combine_syntax_and_fuzzy_match_highlights(
7827 text: &str,
7828 default_style: HighlightStyle,
7829 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
7830 match_indices: &[usize],
7831) -> Vec<(Range<usize>, HighlightStyle)> {
7832 let mut result = Vec::new();
7833 let mut match_indices = match_indices.iter().copied().peekable();
7834
7835 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
7836 {
7837 syntax_highlight.weight = None;
7838
7839 // Add highlights for any fuzzy match characters before the next
7840 // syntax highlight range.
7841 while let Some(&match_index) = match_indices.peek() {
7842 if match_index >= range.start {
7843 break;
7844 }
7845 match_indices.next();
7846 let end_index = char_ix_after(match_index, text);
7847 let mut match_style = default_style;
7848 match_style.weight = Some(fonts::Weight::BOLD);
7849 result.push((match_index..end_index, match_style));
7850 }
7851
7852 if range.start == usize::MAX {
7853 break;
7854 }
7855
7856 // Add highlights for any fuzzy match characters within the
7857 // syntax highlight range.
7858 let mut offset = range.start;
7859 while let Some(&match_index) = match_indices.peek() {
7860 if match_index >= range.end {
7861 break;
7862 }
7863
7864 match_indices.next();
7865 if match_index > offset {
7866 result.push((offset..match_index, syntax_highlight));
7867 }
7868
7869 let mut end_index = char_ix_after(match_index, text);
7870 while let Some(&next_match_index) = match_indices.peek() {
7871 if next_match_index == end_index && next_match_index < range.end {
7872 end_index = char_ix_after(next_match_index, text);
7873 match_indices.next();
7874 } else {
7875 break;
7876 }
7877 }
7878
7879 let mut match_style = syntax_highlight;
7880 match_style.weight = Some(fonts::Weight::BOLD);
7881 result.push((match_index..end_index, match_style));
7882 offset = end_index;
7883 }
7884
7885 if offset < range.end {
7886 result.push((offset..range.end, syntax_highlight));
7887 }
7888 }
7889
7890 fn char_ix_after(ix: usize, text: &str) -> usize {
7891 ix + text[ix..].chars().next().unwrap().len_utf8()
7892 }
7893
7894 result
7895}
7896
7897pub fn styled_runs_for_code_label<'a>(
7898 label: &'a CodeLabel,
7899 syntax_theme: &'a theme::SyntaxTheme,
7900) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
7901 let fade_out = HighlightStyle {
7902 fade_out: Some(0.35),
7903 ..Default::default()
7904 };
7905
7906 let mut prev_end = label.filter_range.end;
7907 label
7908 .runs
7909 .iter()
7910 .enumerate()
7911 .flat_map(move |(ix, (range, highlight_id))| {
7912 let style = if let Some(style) = highlight_id.style(syntax_theme) {
7913 style
7914 } else {
7915 return Default::default();
7916 };
7917 let mut muted_style = style;
7918 muted_style.highlight(fade_out);
7919
7920 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
7921 if range.start >= label.filter_range.end {
7922 if range.start > prev_end {
7923 runs.push((prev_end..range.start, fade_out));
7924 }
7925 runs.push((range.clone(), muted_style));
7926 } else if range.end <= label.filter_range.end {
7927 runs.push((range.clone(), style));
7928 } else {
7929 runs.push((range.start..label.filter_range.end, style));
7930 runs.push((label.filter_range.end..range.end, muted_style));
7931 }
7932 prev_end = cmp::max(prev_end, range.end);
7933
7934 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
7935 runs.push((prev_end..label.text.len(), fade_out));
7936 }
7937
7938 runs
7939 })
7940}
7941
7942pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
7943 let mut index = 0;
7944 let mut codepoints = text.char_indices().peekable();
7945
7946 std::iter::from_fn(move || {
7947 let start_index = index;
7948 while let Some((new_index, codepoint)) = codepoints.next() {
7949 index = new_index + codepoint.len_utf8();
7950 let current_upper = codepoint.is_uppercase();
7951 let next_upper = codepoints
7952 .peek()
7953 .map(|(_, c)| c.is_uppercase())
7954 .unwrap_or(false);
7955
7956 if !current_upper && next_upper {
7957 return Some(&text[start_index..index]);
7958 }
7959 }
7960
7961 index = text.len();
7962 if start_index < text.len() {
7963 return Some(&text[start_index..]);
7964 }
7965 None
7966 })
7967 .flat_map(|word| word.split_inclusive('_'))
7968}
7969
7970trait RangeToAnchorExt {
7971 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
7972}
7973
7974impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7975 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7976 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7977 }
7978}