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