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