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