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 cursor_position = self.selections.newest::<Point>(cx).head();
3959 let snapshot = self.buffer.read(cx).snapshot(cx);
3960 let end_of_line = Point::new(cursor_position.row, snapshot.line_len(cursor_position.row));
3961 let start_of_next_line = end_of_line + Point::new(1, 0);
3962
3963 self.buffer.update(cx, |buffer, cx| {
3964 buffer.edit([(end_of_line..start_of_next_line, " ")], None, cx)
3965 });
3966 }
3967
3968 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3969 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3970 let buffer = &display_map.buffer_snapshot;
3971 let selections = self.selections.all::<Point>(cx);
3972
3973 let mut edits = Vec::new();
3974 let mut selections_iter = selections.iter().peekable();
3975 while let Some(selection) = selections_iter.next() {
3976 // Avoid duplicating the same lines twice.
3977 let mut rows = selection.spanned_rows(false, &display_map);
3978
3979 while let Some(next_selection) = selections_iter.peek() {
3980 let next_rows = next_selection.spanned_rows(false, &display_map);
3981 if next_rows.start < rows.end {
3982 rows.end = next_rows.end;
3983 selections_iter.next().unwrap();
3984 } else {
3985 break;
3986 }
3987 }
3988
3989 // Copy the text from the selected row region and splice it at the start of the region.
3990 let start = Point::new(rows.start, 0);
3991 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3992 let text = buffer
3993 .text_for_range(start..end)
3994 .chain(Some("\n"))
3995 .collect::<String>();
3996 edits.push((start..start, text));
3997 }
3998
3999 self.transact(cx, |this, cx| {
4000 this.buffer.update(cx, |buffer, cx| {
4001 buffer.edit(edits, None, cx);
4002 });
4003
4004 this.request_autoscroll(Autoscroll::fit(), cx);
4005 });
4006 }
4007
4008 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4009 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4010 let buffer = self.buffer.read(cx).snapshot(cx);
4011
4012 let mut edits = Vec::new();
4013 let mut unfold_ranges = Vec::new();
4014 let mut refold_ranges = Vec::new();
4015
4016 let selections = self.selections.all::<Point>(cx);
4017 let mut selections = selections.iter().peekable();
4018 let mut contiguous_row_selections = Vec::new();
4019 let mut new_selections = Vec::new();
4020
4021 while let Some(selection) = selections.next() {
4022 // Find all the selections that span a contiguous row range
4023 let (start_row, end_row) = consume_contiguous_rows(
4024 &mut contiguous_row_selections,
4025 selection,
4026 &display_map,
4027 &mut selections,
4028 );
4029
4030 // Move the text spanned by the row range to be before the line preceding the row range
4031 if start_row > 0 {
4032 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4033 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4034 let insertion_point = display_map
4035 .prev_line_boundary(Point::new(start_row - 1, 0))
4036 .0;
4037
4038 // Don't move lines across excerpts
4039 if buffer
4040 .excerpt_boundaries_in_range((
4041 Bound::Excluded(insertion_point),
4042 Bound::Included(range_to_move.end),
4043 ))
4044 .next()
4045 .is_none()
4046 {
4047 let text = buffer
4048 .text_for_range(range_to_move.clone())
4049 .flat_map(|s| s.chars())
4050 .skip(1)
4051 .chain(['\n'])
4052 .collect::<String>();
4053
4054 edits.push((
4055 buffer.anchor_after(range_to_move.start)
4056 ..buffer.anchor_before(range_to_move.end),
4057 String::new(),
4058 ));
4059 let insertion_anchor = buffer.anchor_after(insertion_point);
4060 edits.push((insertion_anchor..insertion_anchor, text));
4061
4062 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4063
4064 // Move selections up
4065 new_selections.extend(contiguous_row_selections.drain(..).map(
4066 |mut selection| {
4067 selection.start.row -= row_delta;
4068 selection.end.row -= row_delta;
4069 selection
4070 },
4071 ));
4072
4073 // Move folds up
4074 unfold_ranges.push(range_to_move.clone());
4075 for fold in display_map.folds_in_range(
4076 buffer.anchor_before(range_to_move.start)
4077 ..buffer.anchor_after(range_to_move.end),
4078 ) {
4079 let mut start = fold.start.to_point(&buffer);
4080 let mut end = fold.end.to_point(&buffer);
4081 start.row -= row_delta;
4082 end.row -= row_delta;
4083 refold_ranges.push(start..end);
4084 }
4085 }
4086 }
4087
4088 // If we didn't move line(s), preserve the existing selections
4089 new_selections.append(&mut contiguous_row_selections);
4090 }
4091
4092 self.transact(cx, |this, cx| {
4093 this.unfold_ranges(unfold_ranges, true, true, cx);
4094 this.buffer.update(cx, |buffer, cx| {
4095 for (range, text) in edits {
4096 buffer.edit([(range, text)], None, cx);
4097 }
4098 });
4099 this.fold_ranges(refold_ranges, true, cx);
4100 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4101 s.select(new_selections);
4102 })
4103 });
4104 }
4105
4106 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4107 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4108 let buffer = self.buffer.read(cx).snapshot(cx);
4109
4110 let mut edits = Vec::new();
4111 let mut unfold_ranges = Vec::new();
4112 let mut refold_ranges = Vec::new();
4113
4114 let selections = self.selections.all::<Point>(cx);
4115 let mut selections = selections.iter().peekable();
4116 let mut contiguous_row_selections = Vec::new();
4117 let mut new_selections = Vec::new();
4118
4119 while let Some(selection) = selections.next() {
4120 // Find all the selections that span a contiguous row range
4121 let (start_row, end_row) = consume_contiguous_rows(
4122 &mut contiguous_row_selections,
4123 selection,
4124 &display_map,
4125 &mut selections,
4126 );
4127
4128 // Move the text spanned by the row range to be after the last line of the row range
4129 if end_row <= buffer.max_point().row {
4130 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4131 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4132
4133 // Don't move lines across excerpt boundaries
4134 if buffer
4135 .excerpt_boundaries_in_range((
4136 Bound::Excluded(range_to_move.start),
4137 Bound::Included(insertion_point),
4138 ))
4139 .next()
4140 .is_none()
4141 {
4142 let mut text = String::from("\n");
4143 text.extend(buffer.text_for_range(range_to_move.clone()));
4144 text.pop(); // Drop trailing newline
4145 edits.push((
4146 buffer.anchor_after(range_to_move.start)
4147 ..buffer.anchor_before(range_to_move.end),
4148 String::new(),
4149 ));
4150 let insertion_anchor = buffer.anchor_after(insertion_point);
4151 edits.push((insertion_anchor..insertion_anchor, text));
4152
4153 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4154
4155 // Move selections down
4156 new_selections.extend(contiguous_row_selections.drain(..).map(
4157 |mut selection| {
4158 selection.start.row += row_delta;
4159 selection.end.row += row_delta;
4160 selection
4161 },
4162 ));
4163
4164 // Move folds down
4165 unfold_ranges.push(range_to_move.clone());
4166 for fold in display_map.folds_in_range(
4167 buffer.anchor_before(range_to_move.start)
4168 ..buffer.anchor_after(range_to_move.end),
4169 ) {
4170 let mut start = fold.start.to_point(&buffer);
4171 let mut end = fold.end.to_point(&buffer);
4172 start.row += row_delta;
4173 end.row += row_delta;
4174 refold_ranges.push(start..end);
4175 }
4176 }
4177 }
4178
4179 // If we didn't move line(s), preserve the existing selections
4180 new_selections.append(&mut contiguous_row_selections);
4181 }
4182
4183 self.transact(cx, |this, cx| {
4184 this.unfold_ranges(unfold_ranges, true, true, cx);
4185 this.buffer.update(cx, |buffer, cx| {
4186 for (range, text) in edits {
4187 buffer.edit([(range, text)], None, cx);
4188 }
4189 });
4190 this.fold_ranges(refold_ranges, true, cx);
4191 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4192 });
4193 }
4194
4195 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4196 self.transact(cx, |this, cx| {
4197 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4198 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4199 let line_mode = s.line_mode;
4200 s.move_with(|display_map, selection| {
4201 if !selection.is_empty() || line_mode {
4202 return;
4203 }
4204
4205 let mut head = selection.head();
4206 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4207 if head.column() == display_map.line_len(head.row()) {
4208 transpose_offset = display_map
4209 .buffer_snapshot
4210 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4211 }
4212
4213 if transpose_offset == 0 {
4214 return;
4215 }
4216
4217 *head.column_mut() += 1;
4218 head = display_map.clip_point(head, Bias::Right);
4219 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4220
4221 let transpose_start = display_map
4222 .buffer_snapshot
4223 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4224 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4225 let transpose_end = display_map
4226 .buffer_snapshot
4227 .clip_offset(transpose_offset + 1, Bias::Right);
4228 if let Some(ch) =
4229 display_map.buffer_snapshot.chars_at(transpose_start).next()
4230 {
4231 edits.push((transpose_start..transpose_offset, String::new()));
4232 edits.push((transpose_end..transpose_end, ch.to_string()));
4233 }
4234 }
4235 });
4236 edits
4237 });
4238 this.buffer
4239 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4240 let selections = this.selections.all::<usize>(cx);
4241 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4242 s.select(selections);
4243 });
4244 });
4245 }
4246
4247 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4248 let mut text = String::new();
4249 let buffer = self.buffer.read(cx).snapshot(cx);
4250 let mut selections = self.selections.all::<Point>(cx);
4251 let mut clipboard_selections = Vec::with_capacity(selections.len());
4252 {
4253 let max_point = buffer.max_point();
4254 for selection in &mut selections {
4255 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4256 if is_entire_line {
4257 selection.start = Point::new(selection.start.row, 0);
4258 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4259 selection.goal = SelectionGoal::None;
4260 }
4261 let mut len = 0;
4262 for chunk in buffer.text_for_range(selection.start..selection.end) {
4263 text.push_str(chunk);
4264 len += chunk.len();
4265 }
4266 clipboard_selections.push(ClipboardSelection {
4267 len,
4268 is_entire_line,
4269 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4270 });
4271 }
4272 }
4273
4274 self.transact(cx, |this, cx| {
4275 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4276 s.select(selections);
4277 });
4278 this.insert("", cx);
4279 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4280 });
4281 }
4282
4283 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4284 let selections = self.selections.all::<Point>(cx);
4285 let buffer = self.buffer.read(cx).read(cx);
4286 let mut text = String::new();
4287
4288 let mut clipboard_selections = Vec::with_capacity(selections.len());
4289 {
4290 let max_point = buffer.max_point();
4291 for selection in selections.iter() {
4292 let mut start = selection.start;
4293 let mut end = selection.end;
4294 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4295 if is_entire_line {
4296 start = Point::new(start.row, 0);
4297 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4298 }
4299 let mut len = 0;
4300 for chunk in buffer.text_for_range(start..end) {
4301 text.push_str(chunk);
4302 len += chunk.len();
4303 }
4304 clipboard_selections.push(ClipboardSelection {
4305 len,
4306 is_entire_line,
4307 first_line_indent: buffer.indent_size_for_line(start.row).len,
4308 });
4309 }
4310 }
4311
4312 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4313 }
4314
4315 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4316 self.transact(cx, |this, cx| {
4317 if let Some(item) = cx.read_from_clipboard() {
4318 let mut clipboard_text = Cow::Borrowed(item.text());
4319 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4320 let old_selections = this.selections.all::<usize>(cx);
4321 let all_selections_were_entire_line =
4322 clipboard_selections.iter().all(|s| s.is_entire_line);
4323 let first_selection_indent_column =
4324 clipboard_selections.first().map(|s| s.first_line_indent);
4325 if clipboard_selections.len() != old_selections.len() {
4326 let mut newline_separated_text = String::new();
4327 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4328 let mut ix = 0;
4329 while let Some(clipboard_selection) = clipboard_selections.next() {
4330 newline_separated_text
4331 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4332 ix += clipboard_selection.len;
4333 if clipboard_selections.peek().is_some() {
4334 newline_separated_text.push('\n');
4335 }
4336 }
4337 clipboard_text = Cow::Owned(newline_separated_text);
4338 }
4339
4340 this.buffer.update(cx, |buffer, cx| {
4341 let snapshot = buffer.read(cx);
4342 let mut start_offset = 0;
4343 let mut edits = Vec::new();
4344 let mut original_indent_columns = Vec::new();
4345 let line_mode = this.selections.line_mode;
4346 for (ix, selection) in old_selections.iter().enumerate() {
4347 let to_insert;
4348 let entire_line;
4349 let original_indent_column;
4350 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4351 let end_offset = start_offset + clipboard_selection.len;
4352 to_insert = &clipboard_text[start_offset..end_offset];
4353 entire_line = clipboard_selection.is_entire_line;
4354 start_offset = end_offset;
4355 original_indent_column =
4356 Some(clipboard_selection.first_line_indent);
4357 } else {
4358 to_insert = clipboard_text.as_str();
4359 entire_line = all_selections_were_entire_line;
4360 original_indent_column = first_selection_indent_column
4361 }
4362
4363 // If the corresponding selection was empty when this slice of the
4364 // clipboard text was written, then the entire line containing the
4365 // selection was copied. If this selection is also currently empty,
4366 // then paste the line before the current line of the buffer.
4367 let range = if selection.is_empty() && !line_mode && entire_line {
4368 let column = selection.start.to_point(&snapshot).column as usize;
4369 let line_start = selection.start - column;
4370 line_start..line_start
4371 } else {
4372 selection.range()
4373 };
4374
4375 edits.push((range, to_insert));
4376 original_indent_columns.extend(original_indent_column);
4377 }
4378 drop(snapshot);
4379
4380 buffer.edit(
4381 edits,
4382 Some(AutoindentMode::Block {
4383 original_indent_columns,
4384 }),
4385 cx,
4386 );
4387 });
4388
4389 let selections = this.selections.all::<usize>(cx);
4390 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4391 } else {
4392 this.insert(&clipboard_text, cx);
4393 }
4394 }
4395 });
4396 }
4397
4398 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4399 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4400 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4401 self.change_selections(None, cx, |s| {
4402 s.select_anchors(selections.to_vec());
4403 });
4404 }
4405 self.request_autoscroll(Autoscroll::fit(), cx);
4406 self.unmark_text(cx);
4407 self.refresh_copilot_suggestions(true, cx);
4408 cx.emit(Event::Edited);
4409 }
4410 }
4411
4412 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4413 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4414 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4415 {
4416 self.change_selections(None, cx, |s| {
4417 s.select_anchors(selections.to_vec());
4418 });
4419 }
4420 self.request_autoscroll(Autoscroll::fit(), cx);
4421 self.unmark_text(cx);
4422 self.refresh_copilot_suggestions(true, cx);
4423 cx.emit(Event::Edited);
4424 }
4425 }
4426
4427 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4428 self.buffer
4429 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4430 }
4431
4432 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4433 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4434 let line_mode = s.line_mode;
4435 s.move_with(|map, selection| {
4436 let cursor = if selection.is_empty() && !line_mode {
4437 movement::left(map, selection.start)
4438 } else {
4439 selection.start
4440 };
4441 selection.collapse_to(cursor, SelectionGoal::None);
4442 });
4443 })
4444 }
4445
4446 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4447 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4448 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4449 })
4450 }
4451
4452 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4453 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4454 let line_mode = s.line_mode;
4455 s.move_with(|map, selection| {
4456 let cursor = if selection.is_empty() && !line_mode {
4457 movement::right(map, selection.end)
4458 } else {
4459 selection.end
4460 };
4461 selection.collapse_to(cursor, SelectionGoal::None)
4462 });
4463 })
4464 }
4465
4466 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4467 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4468 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4469 })
4470 }
4471
4472 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4473 if self.take_rename(true, cx).is_some() {
4474 return;
4475 }
4476
4477 if let Some(context_menu) = self.context_menu.as_mut() {
4478 if context_menu.select_prev(cx) {
4479 return;
4480 }
4481 }
4482
4483 if matches!(self.mode, EditorMode::SingleLine) {
4484 cx.propagate_action();
4485 return;
4486 }
4487
4488 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4489 let line_mode = s.line_mode;
4490 s.move_with(|map, selection| {
4491 if !selection.is_empty() && !line_mode {
4492 selection.goal = SelectionGoal::None;
4493 }
4494 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4495 selection.collapse_to(cursor, goal);
4496 });
4497 })
4498 }
4499
4500 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4501 if self.take_rename(true, cx).is_some() {
4502 return;
4503 }
4504
4505 if self
4506 .context_menu
4507 .as_mut()
4508 .map(|menu| menu.select_first(cx))
4509 .unwrap_or(false)
4510 {
4511 return;
4512 }
4513
4514 if matches!(self.mode, EditorMode::SingleLine) {
4515 cx.propagate_action();
4516 return;
4517 }
4518
4519 let row_count = if let Some(row_count) = self.visible_line_count() {
4520 row_count as u32 - 1
4521 } else {
4522 return;
4523 };
4524
4525 let autoscroll = if action.center_cursor {
4526 Autoscroll::center()
4527 } else {
4528 Autoscroll::fit()
4529 };
4530
4531 self.change_selections(Some(autoscroll), cx, |s| {
4532 let line_mode = s.line_mode;
4533 s.move_with(|map, selection| {
4534 if !selection.is_empty() && !line_mode {
4535 selection.goal = SelectionGoal::None;
4536 }
4537 let (cursor, goal) =
4538 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4539 selection.collapse_to(cursor, goal);
4540 });
4541 });
4542 }
4543
4544 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4545 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4546 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4547 })
4548 }
4549
4550 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4551 self.take_rename(true, cx);
4552
4553 if let Some(context_menu) = self.context_menu.as_mut() {
4554 if context_menu.select_next(cx) {
4555 return;
4556 }
4557 }
4558
4559 if self.mode == EditorMode::SingleLine {
4560 cx.propagate_action();
4561 return;
4562 }
4563
4564 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4565 let line_mode = s.line_mode;
4566 s.move_with(|map, selection| {
4567 if !selection.is_empty() && !line_mode {
4568 selection.goal = SelectionGoal::None;
4569 }
4570 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4571 selection.collapse_to(cursor, goal);
4572 });
4573 });
4574 }
4575
4576 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4577 if self.take_rename(true, cx).is_some() {
4578 return;
4579 }
4580
4581 if self
4582 .context_menu
4583 .as_mut()
4584 .map(|menu| menu.select_last(cx))
4585 .unwrap_or(false)
4586 {
4587 return;
4588 }
4589
4590 if matches!(self.mode, EditorMode::SingleLine) {
4591 cx.propagate_action();
4592 return;
4593 }
4594
4595 let row_count = if let Some(row_count) = self.visible_line_count() {
4596 row_count as u32 - 1
4597 } else {
4598 return;
4599 };
4600
4601 let autoscroll = if action.center_cursor {
4602 Autoscroll::center()
4603 } else {
4604 Autoscroll::fit()
4605 };
4606
4607 self.change_selections(Some(autoscroll), cx, |s| {
4608 let line_mode = s.line_mode;
4609 s.move_with(|map, selection| {
4610 if !selection.is_empty() && !line_mode {
4611 selection.goal = SelectionGoal::None;
4612 }
4613 let (cursor, goal) =
4614 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4615 selection.collapse_to(cursor, goal);
4616 });
4617 });
4618 }
4619
4620 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4621 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4622 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4623 });
4624 }
4625
4626 pub fn move_to_previous_word_start(
4627 &mut self,
4628 _: &MoveToPreviousWordStart,
4629 cx: &mut ViewContext<Self>,
4630 ) {
4631 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4632 s.move_cursors_with(|map, head, _| {
4633 (
4634 movement::previous_word_start(map, head),
4635 SelectionGoal::None,
4636 )
4637 });
4638 })
4639 }
4640
4641 pub fn move_to_previous_subword_start(
4642 &mut self,
4643 _: &MoveToPreviousSubwordStart,
4644 cx: &mut ViewContext<Self>,
4645 ) {
4646 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4647 s.move_cursors_with(|map, head, _| {
4648 (
4649 movement::previous_subword_start(map, head),
4650 SelectionGoal::None,
4651 )
4652 });
4653 })
4654 }
4655
4656 pub fn select_to_previous_word_start(
4657 &mut self,
4658 _: &SelectToPreviousWordStart,
4659 cx: &mut ViewContext<Self>,
4660 ) {
4661 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4662 s.move_heads_with(|map, head, _| {
4663 (
4664 movement::previous_word_start(map, head),
4665 SelectionGoal::None,
4666 )
4667 });
4668 })
4669 }
4670
4671 pub fn select_to_previous_subword_start(
4672 &mut self,
4673 _: &SelectToPreviousSubwordStart,
4674 cx: &mut ViewContext<Self>,
4675 ) {
4676 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4677 s.move_heads_with(|map, head, _| {
4678 (
4679 movement::previous_subword_start(map, head),
4680 SelectionGoal::None,
4681 )
4682 });
4683 })
4684 }
4685
4686 pub fn delete_to_previous_word_start(
4687 &mut self,
4688 _: &DeleteToPreviousWordStart,
4689 cx: &mut ViewContext<Self>,
4690 ) {
4691 self.transact(cx, |this, cx| {
4692 this.select_autoclose_pair(cx);
4693 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4694 let line_mode = s.line_mode;
4695 s.move_with(|map, selection| {
4696 if selection.is_empty() && !line_mode {
4697 let cursor = movement::previous_word_start(map, selection.head());
4698 selection.set_head(cursor, SelectionGoal::None);
4699 }
4700 });
4701 });
4702 this.insert("", cx);
4703 });
4704 }
4705
4706 pub fn delete_to_previous_subword_start(
4707 &mut self,
4708 _: &DeleteToPreviousSubwordStart,
4709 cx: &mut ViewContext<Self>,
4710 ) {
4711 self.transact(cx, |this, cx| {
4712 this.select_autoclose_pair(cx);
4713 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4714 let line_mode = s.line_mode;
4715 s.move_with(|map, selection| {
4716 if selection.is_empty() && !line_mode {
4717 let cursor = movement::previous_subword_start(map, selection.head());
4718 selection.set_head(cursor, SelectionGoal::None);
4719 }
4720 });
4721 });
4722 this.insert("", cx);
4723 });
4724 }
4725
4726 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4727 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4728 s.move_cursors_with(|map, head, _| {
4729 (movement::next_word_end(map, head), SelectionGoal::None)
4730 });
4731 })
4732 }
4733
4734 pub fn move_to_next_subword_end(
4735 &mut self,
4736 _: &MoveToNextSubwordEnd,
4737 cx: &mut ViewContext<Self>,
4738 ) {
4739 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4740 s.move_cursors_with(|map, head, _| {
4741 (movement::next_subword_end(map, head), SelectionGoal::None)
4742 });
4743 })
4744 }
4745
4746 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4747 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4748 s.move_heads_with(|map, head, _| {
4749 (movement::next_word_end(map, head), SelectionGoal::None)
4750 });
4751 })
4752 }
4753
4754 pub fn select_to_next_subword_end(
4755 &mut self,
4756 _: &SelectToNextSubwordEnd,
4757 cx: &mut ViewContext<Self>,
4758 ) {
4759 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4760 s.move_heads_with(|map, head, _| {
4761 (movement::next_subword_end(map, head), SelectionGoal::None)
4762 });
4763 })
4764 }
4765
4766 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4767 self.transact(cx, |this, cx| {
4768 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4769 let line_mode = s.line_mode;
4770 s.move_with(|map, selection| {
4771 if selection.is_empty() && !line_mode {
4772 let cursor = movement::next_word_end(map, selection.head());
4773 selection.set_head(cursor, SelectionGoal::None);
4774 }
4775 });
4776 });
4777 this.insert("", cx);
4778 });
4779 }
4780
4781 pub fn delete_to_next_subword_end(
4782 &mut self,
4783 _: &DeleteToNextSubwordEnd,
4784 cx: &mut ViewContext<Self>,
4785 ) {
4786 self.transact(cx, |this, cx| {
4787 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4788 s.move_with(|map, selection| {
4789 if selection.is_empty() {
4790 let cursor = movement::next_subword_end(map, selection.head());
4791 selection.set_head(cursor, SelectionGoal::None);
4792 }
4793 });
4794 });
4795 this.insert("", cx);
4796 });
4797 }
4798
4799 pub fn move_to_beginning_of_line(
4800 &mut self,
4801 _: &MoveToBeginningOfLine,
4802 cx: &mut ViewContext<Self>,
4803 ) {
4804 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4805 s.move_cursors_with(|map, head, _| {
4806 (
4807 movement::indented_line_beginning(map, head, true),
4808 SelectionGoal::None,
4809 )
4810 });
4811 })
4812 }
4813
4814 pub fn select_to_beginning_of_line(
4815 &mut self,
4816 action: &SelectToBeginningOfLine,
4817 cx: &mut ViewContext<Self>,
4818 ) {
4819 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4820 s.move_heads_with(|map, head, _| {
4821 (
4822 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4823 SelectionGoal::None,
4824 )
4825 });
4826 });
4827 }
4828
4829 pub fn delete_to_beginning_of_line(
4830 &mut self,
4831 _: &DeleteToBeginningOfLine,
4832 cx: &mut ViewContext<Self>,
4833 ) {
4834 self.transact(cx, |this, cx| {
4835 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4836 s.move_with(|_, selection| {
4837 selection.reversed = true;
4838 });
4839 });
4840
4841 this.select_to_beginning_of_line(
4842 &SelectToBeginningOfLine {
4843 stop_at_soft_wraps: false,
4844 },
4845 cx,
4846 );
4847 this.backspace(&Backspace, cx);
4848 });
4849 }
4850
4851 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4852 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4853 s.move_cursors_with(|map, head, _| {
4854 (movement::line_end(map, head, true), SelectionGoal::None)
4855 });
4856 })
4857 }
4858
4859 pub fn select_to_end_of_line(
4860 &mut self,
4861 action: &SelectToEndOfLine,
4862 cx: &mut ViewContext<Self>,
4863 ) {
4864 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4865 s.move_heads_with(|map, head, _| {
4866 (
4867 movement::line_end(map, head, action.stop_at_soft_wraps),
4868 SelectionGoal::None,
4869 )
4870 });
4871 })
4872 }
4873
4874 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4875 self.transact(cx, |this, cx| {
4876 this.select_to_end_of_line(
4877 &SelectToEndOfLine {
4878 stop_at_soft_wraps: false,
4879 },
4880 cx,
4881 );
4882 this.delete(&Delete, cx);
4883 });
4884 }
4885
4886 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
4887 self.transact(cx, |this, cx| {
4888 this.select_to_end_of_line(
4889 &SelectToEndOfLine {
4890 stop_at_soft_wraps: false,
4891 },
4892 cx,
4893 );
4894 this.cut(&Cut, cx);
4895 });
4896 }
4897
4898 pub fn move_to_start_of_paragraph(
4899 &mut self,
4900 _: &MoveToStartOfParagraph,
4901 cx: &mut ViewContext<Self>,
4902 ) {
4903 if matches!(self.mode, EditorMode::SingleLine) {
4904 cx.propagate_action();
4905 return;
4906 }
4907
4908 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4909 s.move_with(|map, selection| {
4910 selection.collapse_to(
4911 movement::start_of_paragraph(map, selection.head()),
4912 SelectionGoal::None,
4913 )
4914 });
4915 })
4916 }
4917
4918 pub fn move_to_end_of_paragraph(
4919 &mut self,
4920 _: &MoveToEndOfParagraph,
4921 cx: &mut ViewContext<Self>,
4922 ) {
4923 if matches!(self.mode, EditorMode::SingleLine) {
4924 cx.propagate_action();
4925 return;
4926 }
4927
4928 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4929 s.move_with(|map, selection| {
4930 selection.collapse_to(
4931 movement::end_of_paragraph(map, selection.head()),
4932 SelectionGoal::None,
4933 )
4934 });
4935 })
4936 }
4937
4938 pub fn select_to_start_of_paragraph(
4939 &mut self,
4940 _: &SelectToStartOfParagraph,
4941 cx: &mut ViewContext<Self>,
4942 ) {
4943 if matches!(self.mode, EditorMode::SingleLine) {
4944 cx.propagate_action();
4945 return;
4946 }
4947
4948 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4949 s.move_heads_with(|map, head, _| {
4950 (movement::start_of_paragraph(map, head), SelectionGoal::None)
4951 });
4952 })
4953 }
4954
4955 pub fn select_to_end_of_paragraph(
4956 &mut self,
4957 _: &SelectToEndOfParagraph,
4958 cx: &mut ViewContext<Self>,
4959 ) {
4960 if matches!(self.mode, EditorMode::SingleLine) {
4961 cx.propagate_action();
4962 return;
4963 }
4964
4965 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4966 s.move_heads_with(|map, head, _| {
4967 (movement::end_of_paragraph(map, head), SelectionGoal::None)
4968 });
4969 })
4970 }
4971
4972 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4973 if matches!(self.mode, EditorMode::SingleLine) {
4974 cx.propagate_action();
4975 return;
4976 }
4977
4978 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4979 s.select_ranges(vec![0..0]);
4980 });
4981 }
4982
4983 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4984 let mut selection = self.selections.last::<Point>(cx);
4985 selection.set_head(Point::zero(), SelectionGoal::None);
4986
4987 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4988 s.select(vec![selection]);
4989 });
4990 }
4991
4992 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4993 if matches!(self.mode, EditorMode::SingleLine) {
4994 cx.propagate_action();
4995 return;
4996 }
4997
4998 let cursor = self.buffer.read(cx).read(cx).len();
4999 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5000 s.select_ranges(vec![cursor..cursor])
5001 });
5002 }
5003
5004 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5005 self.nav_history = nav_history;
5006 }
5007
5008 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5009 self.nav_history.as_ref()
5010 }
5011
5012 fn push_to_nav_history(
5013 &mut self,
5014 cursor_anchor: Anchor,
5015 new_position: Option<Point>,
5016 cx: &mut ViewContext<Self>,
5017 ) {
5018 if let Some(nav_history) = self.nav_history.as_mut() {
5019 let buffer = self.buffer.read(cx).read(cx);
5020 let cursor_position = cursor_anchor.to_point(&buffer);
5021 let scroll_state = self.scroll_manager.anchor();
5022 let scroll_top_row = scroll_state.top_row(&buffer);
5023 drop(buffer);
5024
5025 if let Some(new_position) = new_position {
5026 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5027 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5028 return;
5029 }
5030 }
5031
5032 nav_history.push(
5033 Some(NavigationData {
5034 cursor_anchor,
5035 cursor_position,
5036 scroll_anchor: scroll_state,
5037 scroll_top_row,
5038 }),
5039 cx,
5040 );
5041 }
5042 }
5043
5044 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5045 let buffer = self.buffer.read(cx).snapshot(cx);
5046 let mut selection = self.selections.first::<usize>(cx);
5047 selection.set_head(buffer.len(), SelectionGoal::None);
5048 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5049 s.select(vec![selection]);
5050 });
5051 }
5052
5053 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5054 let end = self.buffer.read(cx).read(cx).len();
5055 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5056 s.select_ranges(vec![0..end]);
5057 });
5058 }
5059
5060 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5061 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5062 let mut selections = self.selections.all::<Point>(cx);
5063 let max_point = display_map.buffer_snapshot.max_point();
5064 for selection in &mut selections {
5065 let rows = selection.spanned_rows(true, &display_map);
5066 selection.start = Point::new(rows.start, 0);
5067 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5068 selection.reversed = false;
5069 }
5070 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5071 s.select(selections);
5072 });
5073 }
5074
5075 pub fn split_selection_into_lines(
5076 &mut self,
5077 _: &SplitSelectionIntoLines,
5078 cx: &mut ViewContext<Self>,
5079 ) {
5080 let mut to_unfold = Vec::new();
5081 let mut new_selection_ranges = Vec::new();
5082 {
5083 let selections = self.selections.all::<Point>(cx);
5084 let buffer = self.buffer.read(cx).read(cx);
5085 for selection in selections {
5086 for row in selection.start.row..selection.end.row {
5087 let cursor = Point::new(row, buffer.line_len(row));
5088 new_selection_ranges.push(cursor..cursor);
5089 }
5090 new_selection_ranges.push(selection.end..selection.end);
5091 to_unfold.push(selection.start..selection.end);
5092 }
5093 }
5094 self.unfold_ranges(to_unfold, true, true, cx);
5095 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5096 s.select_ranges(new_selection_ranges);
5097 });
5098 }
5099
5100 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5101 self.add_selection(true, cx);
5102 }
5103
5104 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5105 self.add_selection(false, cx);
5106 }
5107
5108 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5109 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5110 let mut selections = self.selections.all::<Point>(cx);
5111 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5112 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5113 let range = oldest_selection.display_range(&display_map).sorted();
5114 let columns = cmp::min(range.start.column(), range.end.column())
5115 ..cmp::max(range.start.column(), range.end.column());
5116
5117 selections.clear();
5118 let mut stack = Vec::new();
5119 for row in range.start.row()..=range.end.row() {
5120 if let Some(selection) = self.selections.build_columnar_selection(
5121 &display_map,
5122 row,
5123 &columns,
5124 oldest_selection.reversed,
5125 ) {
5126 stack.push(selection.id);
5127 selections.push(selection);
5128 }
5129 }
5130
5131 if above {
5132 stack.reverse();
5133 }
5134
5135 AddSelectionsState { above, stack }
5136 });
5137
5138 let last_added_selection = *state.stack.last().unwrap();
5139 let mut new_selections = Vec::new();
5140 if above == state.above {
5141 let end_row = if above {
5142 0
5143 } else {
5144 display_map.max_point().row()
5145 };
5146
5147 'outer: for selection in selections {
5148 if selection.id == last_added_selection {
5149 let range = selection.display_range(&display_map).sorted();
5150 debug_assert_eq!(range.start.row(), range.end.row());
5151 let mut row = range.start.row();
5152 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5153 {
5154 start..end
5155 } else {
5156 cmp::min(range.start.column(), range.end.column())
5157 ..cmp::max(range.start.column(), range.end.column())
5158 };
5159
5160 while row != end_row {
5161 if above {
5162 row -= 1;
5163 } else {
5164 row += 1;
5165 }
5166
5167 if let Some(new_selection) = self.selections.build_columnar_selection(
5168 &display_map,
5169 row,
5170 &columns,
5171 selection.reversed,
5172 ) {
5173 state.stack.push(new_selection.id);
5174 if above {
5175 new_selections.push(new_selection);
5176 new_selections.push(selection);
5177 } else {
5178 new_selections.push(selection);
5179 new_selections.push(new_selection);
5180 }
5181
5182 continue 'outer;
5183 }
5184 }
5185 }
5186
5187 new_selections.push(selection);
5188 }
5189 } else {
5190 new_selections = selections;
5191 new_selections.retain(|s| s.id != last_added_selection);
5192 state.stack.pop();
5193 }
5194
5195 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5196 s.select(new_selections);
5197 });
5198 if state.stack.len() > 1 {
5199 self.add_selections_state = Some(state);
5200 }
5201 }
5202
5203 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5204 self.push_to_selection_history();
5205 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5206 let buffer = &display_map.buffer_snapshot;
5207 let mut selections = self.selections.all::<usize>(cx);
5208 if let Some(mut select_next_state) = self.select_next_state.take() {
5209 let query = &select_next_state.query;
5210 if !select_next_state.done {
5211 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5212 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5213 let mut next_selected_range = None;
5214
5215 let bytes_after_last_selection =
5216 buffer.bytes_in_range(last_selection.end..buffer.len());
5217 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5218 let query_matches = query
5219 .stream_find_iter(bytes_after_last_selection)
5220 .map(|result| (last_selection.end, result))
5221 .chain(
5222 query
5223 .stream_find_iter(bytes_before_first_selection)
5224 .map(|result| (0, result)),
5225 );
5226 for (start_offset, query_match) in query_matches {
5227 let query_match = query_match.unwrap(); // can only fail due to I/O
5228 let offset_range =
5229 start_offset + query_match.start()..start_offset + query_match.end();
5230 let display_range = offset_range.start.to_display_point(&display_map)
5231 ..offset_range.end.to_display_point(&display_map);
5232
5233 if !select_next_state.wordwise
5234 || (!movement::is_inside_word(&display_map, display_range.start)
5235 && !movement::is_inside_word(&display_map, display_range.end))
5236 {
5237 next_selected_range = Some(offset_range);
5238 break;
5239 }
5240 }
5241
5242 if let Some(next_selected_range) = next_selected_range {
5243 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5244 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5245 if action.replace_newest {
5246 s.delete(s.newest_anchor().id);
5247 }
5248 s.insert_range(next_selected_range);
5249 });
5250 } else {
5251 select_next_state.done = true;
5252 }
5253 }
5254
5255 self.select_next_state = Some(select_next_state);
5256 } else if selections.len() == 1 {
5257 let selection = selections.last_mut().unwrap();
5258 if selection.start == selection.end {
5259 let word_range = movement::surrounding_word(
5260 &display_map,
5261 selection.start.to_display_point(&display_map),
5262 );
5263 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5264 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5265 selection.goal = SelectionGoal::None;
5266 selection.reversed = false;
5267
5268 let query = buffer
5269 .text_for_range(selection.start..selection.end)
5270 .collect::<String>();
5271 let select_state = SelectNextState {
5272 query: AhoCorasick::new_auto_configured(&[query]),
5273 wordwise: true,
5274 done: false,
5275 };
5276 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5277 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5278 s.select(selections);
5279 });
5280 self.select_next_state = Some(select_state);
5281 } else {
5282 let query = buffer
5283 .text_for_range(selection.start..selection.end)
5284 .collect::<String>();
5285 self.select_next_state = Some(SelectNextState {
5286 query: AhoCorasick::new_auto_configured(&[query]),
5287 wordwise: false,
5288 done: false,
5289 });
5290 self.select_next(action, cx);
5291 }
5292 }
5293 }
5294
5295 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5296 self.push_to_selection_history();
5297 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5298 let buffer = &display_map.buffer_snapshot;
5299 let mut selections = self.selections.all::<usize>(cx);
5300 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5301 let query = &select_prev_state.query;
5302 if !select_prev_state.done {
5303 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5304 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5305 let mut next_selected_range = None;
5306 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5307 let bytes_before_last_selection =
5308 buffer.reversed_bytes_in_range(0..last_selection.start);
5309 let bytes_after_first_selection =
5310 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5311 let query_matches = query
5312 .stream_find_iter(bytes_before_last_selection)
5313 .map(|result| (last_selection.start, result))
5314 .chain(
5315 query
5316 .stream_find_iter(bytes_after_first_selection)
5317 .map(|result| (buffer.len(), result)),
5318 );
5319 for (end_offset, query_match) in query_matches {
5320 let query_match = query_match.unwrap(); // can only fail due to I/O
5321 let offset_range =
5322 end_offset - query_match.end()..end_offset - query_match.start();
5323 let display_range = offset_range.start.to_display_point(&display_map)
5324 ..offset_range.end.to_display_point(&display_map);
5325
5326 if !select_prev_state.wordwise
5327 || (!movement::is_inside_word(&display_map, display_range.start)
5328 && !movement::is_inside_word(&display_map, display_range.end))
5329 {
5330 next_selected_range = Some(offset_range);
5331 break;
5332 }
5333 }
5334
5335 if let Some(next_selected_range) = next_selected_range {
5336 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5337 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5338 if action.replace_newest {
5339 s.delete(s.newest_anchor().id);
5340 }
5341 s.insert_range(next_selected_range);
5342 });
5343 } else {
5344 select_prev_state.done = true;
5345 }
5346 }
5347
5348 self.select_prev_state = Some(select_prev_state);
5349 } else if selections.len() == 1 {
5350 let selection = selections.last_mut().unwrap();
5351 if selection.start == selection.end {
5352 let word_range = movement::surrounding_word(
5353 &display_map,
5354 selection.start.to_display_point(&display_map),
5355 );
5356 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5357 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5358 selection.goal = SelectionGoal::None;
5359 selection.reversed = false;
5360
5361 let query = buffer
5362 .text_for_range(selection.start..selection.end)
5363 .collect::<String>();
5364 let query = query.chars().rev().collect::<String>();
5365 let select_state = SelectNextState {
5366 query: AhoCorasick::new_auto_configured(&[query]),
5367 wordwise: true,
5368 done: false,
5369 };
5370 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5371 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5372 s.select(selections);
5373 });
5374 self.select_prev_state = Some(select_state);
5375 } else {
5376 let query = buffer
5377 .text_for_range(selection.start..selection.end)
5378 .collect::<String>();
5379 let query = query.chars().rev().collect::<String>();
5380 self.select_prev_state = Some(SelectNextState {
5381 query: AhoCorasick::new_auto_configured(&[query]),
5382 wordwise: false,
5383 done: false,
5384 });
5385 self.select_previous(action, cx);
5386 }
5387 }
5388 }
5389
5390 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5391 self.transact(cx, |this, cx| {
5392 let mut selections = this.selections.all::<Point>(cx);
5393 let mut edits = Vec::new();
5394 let mut selection_edit_ranges = Vec::new();
5395 let mut last_toggled_row = None;
5396 let snapshot = this.buffer.read(cx).read(cx);
5397 let empty_str: Arc<str> = "".into();
5398 let mut suffixes_inserted = Vec::new();
5399
5400 fn comment_prefix_range(
5401 snapshot: &MultiBufferSnapshot,
5402 row: u32,
5403 comment_prefix: &str,
5404 comment_prefix_whitespace: &str,
5405 ) -> Range<Point> {
5406 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5407
5408 let mut line_bytes = snapshot
5409 .bytes_in_range(start..snapshot.max_point())
5410 .flatten()
5411 .copied();
5412
5413 // If this line currently begins with the line comment prefix, then record
5414 // the range containing the prefix.
5415 if line_bytes
5416 .by_ref()
5417 .take(comment_prefix.len())
5418 .eq(comment_prefix.bytes())
5419 {
5420 // Include any whitespace that matches the comment prefix.
5421 let matching_whitespace_len = line_bytes
5422 .zip(comment_prefix_whitespace.bytes())
5423 .take_while(|(a, b)| a == b)
5424 .count() as u32;
5425 let end = Point::new(
5426 start.row,
5427 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5428 );
5429 start..end
5430 } else {
5431 start..start
5432 }
5433 }
5434
5435 fn comment_suffix_range(
5436 snapshot: &MultiBufferSnapshot,
5437 row: u32,
5438 comment_suffix: &str,
5439 comment_suffix_has_leading_space: bool,
5440 ) -> Range<Point> {
5441 let end = Point::new(row, snapshot.line_len(row));
5442 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5443
5444 let mut line_end_bytes = snapshot
5445 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5446 .flatten()
5447 .copied();
5448
5449 let leading_space_len = if suffix_start_column > 0
5450 && line_end_bytes.next() == Some(b' ')
5451 && comment_suffix_has_leading_space
5452 {
5453 1
5454 } else {
5455 0
5456 };
5457
5458 // If this line currently begins with the line comment prefix, then record
5459 // the range containing the prefix.
5460 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5461 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5462 start..end
5463 } else {
5464 end..end
5465 }
5466 }
5467
5468 // TODO: Handle selections that cross excerpts
5469 for selection in &mut selections {
5470 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5471 let language = if let Some(language) =
5472 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5473 {
5474 language
5475 } else {
5476 continue;
5477 };
5478
5479 selection_edit_ranges.clear();
5480
5481 // If multiple selections contain a given row, avoid processing that
5482 // row more than once.
5483 let mut start_row = selection.start.row;
5484 if last_toggled_row == Some(start_row) {
5485 start_row += 1;
5486 }
5487 let end_row =
5488 if selection.end.row > selection.start.row && selection.end.column == 0 {
5489 selection.end.row - 1
5490 } else {
5491 selection.end.row
5492 };
5493 last_toggled_row = Some(end_row);
5494
5495 if start_row > end_row {
5496 continue;
5497 }
5498
5499 // If the language has line comments, toggle those.
5500 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5501 // Split the comment prefix's trailing whitespace into a separate string,
5502 // as that portion won't be used for detecting if a line is a comment.
5503 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5504 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5505 let mut all_selection_lines_are_comments = true;
5506
5507 for row in start_row..=end_row {
5508 if snapshot.is_line_blank(row) && start_row < end_row {
5509 continue;
5510 }
5511
5512 let prefix_range = comment_prefix_range(
5513 snapshot.deref(),
5514 row,
5515 comment_prefix,
5516 comment_prefix_whitespace,
5517 );
5518 if prefix_range.is_empty() {
5519 all_selection_lines_are_comments = false;
5520 }
5521 selection_edit_ranges.push(prefix_range);
5522 }
5523
5524 if all_selection_lines_are_comments {
5525 edits.extend(
5526 selection_edit_ranges
5527 .iter()
5528 .cloned()
5529 .map(|range| (range, empty_str.clone())),
5530 );
5531 } else {
5532 let min_column = selection_edit_ranges
5533 .iter()
5534 .map(|r| r.start.column)
5535 .min()
5536 .unwrap_or(0);
5537 edits.extend(selection_edit_ranges.iter().map(|range| {
5538 let position = Point::new(range.start.row, min_column);
5539 (position..position, full_comment_prefix.clone())
5540 }));
5541 }
5542 } else if let Some((full_comment_prefix, comment_suffix)) =
5543 language.block_comment_delimiters()
5544 {
5545 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5546 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5547 let prefix_range = comment_prefix_range(
5548 snapshot.deref(),
5549 start_row,
5550 comment_prefix,
5551 comment_prefix_whitespace,
5552 );
5553 let suffix_range = comment_suffix_range(
5554 snapshot.deref(),
5555 end_row,
5556 comment_suffix.trim_start_matches(' '),
5557 comment_suffix.starts_with(' '),
5558 );
5559
5560 if prefix_range.is_empty() || suffix_range.is_empty() {
5561 edits.push((
5562 prefix_range.start..prefix_range.start,
5563 full_comment_prefix.clone(),
5564 ));
5565 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5566 suffixes_inserted.push((end_row, comment_suffix.len()));
5567 } else {
5568 edits.push((prefix_range, empty_str.clone()));
5569 edits.push((suffix_range, empty_str.clone()));
5570 }
5571 } else {
5572 continue;
5573 }
5574 }
5575
5576 drop(snapshot);
5577 this.buffer.update(cx, |buffer, cx| {
5578 buffer.edit(edits, None, cx);
5579 });
5580
5581 // Adjust selections so that they end before any comment suffixes that
5582 // were inserted.
5583 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5584 let mut selections = this.selections.all::<Point>(cx);
5585 let snapshot = this.buffer.read(cx).read(cx);
5586 for selection in &mut selections {
5587 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5588 match row.cmp(&selection.end.row) {
5589 Ordering::Less => {
5590 suffixes_inserted.next();
5591 continue;
5592 }
5593 Ordering::Greater => break,
5594 Ordering::Equal => {
5595 if selection.end.column == snapshot.line_len(row) {
5596 if selection.is_empty() {
5597 selection.start.column -= suffix_len as u32;
5598 }
5599 selection.end.column -= suffix_len as u32;
5600 }
5601 break;
5602 }
5603 }
5604 }
5605 }
5606
5607 drop(snapshot);
5608 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5609
5610 let selections = this.selections.all::<Point>(cx);
5611 let selections_on_single_row = selections.windows(2).all(|selections| {
5612 selections[0].start.row == selections[1].start.row
5613 && selections[0].end.row == selections[1].end.row
5614 && selections[0].start.row == selections[0].end.row
5615 });
5616 let selections_selecting = selections
5617 .iter()
5618 .any(|selection| selection.start != selection.end);
5619 let advance_downwards = action.advance_downwards
5620 && selections_on_single_row
5621 && !selections_selecting
5622 && this.mode != EditorMode::SingleLine;
5623
5624 if advance_downwards {
5625 let snapshot = this.buffer.read(cx).snapshot(cx);
5626
5627 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5628 s.move_cursors_with(|display_snapshot, display_point, _| {
5629 let mut point = display_point.to_point(display_snapshot);
5630 point.row += 1;
5631 point = snapshot.clip_point(point, Bias::Left);
5632 let display_point = point.to_display_point(display_snapshot);
5633 (display_point, SelectionGoal::Column(display_point.column()))
5634 })
5635 });
5636 }
5637 });
5638 }
5639
5640 pub fn select_larger_syntax_node(
5641 &mut self,
5642 _: &SelectLargerSyntaxNode,
5643 cx: &mut ViewContext<Self>,
5644 ) {
5645 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5646 let buffer = self.buffer.read(cx).snapshot(cx);
5647 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5648
5649 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5650 let mut selected_larger_node = false;
5651 let new_selections = old_selections
5652 .iter()
5653 .map(|selection| {
5654 let old_range = selection.start..selection.end;
5655 let mut new_range = old_range.clone();
5656 while let Some(containing_range) =
5657 buffer.range_for_syntax_ancestor(new_range.clone())
5658 {
5659 new_range = containing_range;
5660 if !display_map.intersects_fold(new_range.start)
5661 && !display_map.intersects_fold(new_range.end)
5662 {
5663 break;
5664 }
5665 }
5666
5667 selected_larger_node |= new_range != old_range;
5668 Selection {
5669 id: selection.id,
5670 start: new_range.start,
5671 end: new_range.end,
5672 goal: SelectionGoal::None,
5673 reversed: selection.reversed,
5674 }
5675 })
5676 .collect::<Vec<_>>();
5677
5678 if selected_larger_node {
5679 stack.push(old_selections);
5680 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5681 s.select(new_selections);
5682 });
5683 }
5684 self.select_larger_syntax_node_stack = stack;
5685 }
5686
5687 pub fn select_smaller_syntax_node(
5688 &mut self,
5689 _: &SelectSmallerSyntaxNode,
5690 cx: &mut ViewContext<Self>,
5691 ) {
5692 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5693 if let Some(selections) = stack.pop() {
5694 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5695 s.select(selections.to_vec());
5696 });
5697 }
5698 self.select_larger_syntax_node_stack = stack;
5699 }
5700
5701 pub fn move_to_enclosing_bracket(
5702 &mut self,
5703 _: &MoveToEnclosingBracket,
5704 cx: &mut ViewContext<Self>,
5705 ) {
5706 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5707 s.move_offsets_with(|snapshot, selection| {
5708 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5709 return;
5710 };
5711
5712 let mut best_length = usize::MAX;
5713 let mut best_inside = false;
5714 let mut best_in_bracket_range = false;
5715 let mut best_destination = None;
5716 for (open, close) in enclosing_bracket_ranges {
5717 let close = close.to_inclusive();
5718 let length = close.end() - open.start;
5719 let inside = selection.start >= open.end && selection.end <= *close.start();
5720 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5721
5722 // If best is next to a bracket and current isn't, skip
5723 if !in_bracket_range && best_in_bracket_range {
5724 continue;
5725 }
5726
5727 // Prefer smaller lengths unless best is inside and current isn't
5728 if length > best_length && (best_inside || !inside) {
5729 continue;
5730 }
5731
5732 best_length = length;
5733 best_inside = inside;
5734 best_in_bracket_range = in_bracket_range;
5735 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5736 if inside {
5737 open.end
5738 } else {
5739 open.start
5740 }
5741 } else {
5742 if inside {
5743 *close.start()
5744 } else {
5745 *close.end()
5746 }
5747 });
5748 }
5749
5750 if let Some(destination) = best_destination {
5751 selection.collapse_to(destination, SelectionGoal::None);
5752 }
5753 })
5754 });
5755 }
5756
5757 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5758 self.end_selection(cx);
5759 self.selection_history.mode = SelectionHistoryMode::Undoing;
5760 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5761 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5762 self.select_next_state = entry.select_next_state;
5763 self.select_prev_state = entry.select_prev_state;
5764 self.add_selections_state = entry.add_selections_state;
5765 self.request_autoscroll(Autoscroll::newest(), cx);
5766 }
5767 self.selection_history.mode = SelectionHistoryMode::Normal;
5768 }
5769
5770 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5771 self.end_selection(cx);
5772 self.selection_history.mode = SelectionHistoryMode::Redoing;
5773 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5774 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5775 self.select_next_state = entry.select_next_state;
5776 self.select_prev_state = entry.select_prev_state;
5777 self.add_selections_state = entry.add_selections_state;
5778 self.request_autoscroll(Autoscroll::newest(), cx);
5779 }
5780 self.selection_history.mode = SelectionHistoryMode::Normal;
5781 }
5782
5783 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5784 self.go_to_diagnostic_impl(Direction::Next, cx)
5785 }
5786
5787 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5788 self.go_to_diagnostic_impl(Direction::Prev, cx)
5789 }
5790
5791 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5792 let buffer = self.buffer.read(cx).snapshot(cx);
5793 let selection = self.selections.newest::<usize>(cx);
5794
5795 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5796 if direction == Direction::Next {
5797 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5798 let (group_id, jump_to) = popover.activation_info();
5799 if self.activate_diagnostics(group_id, cx) {
5800 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5801 let mut new_selection = s.newest_anchor().clone();
5802 new_selection.collapse_to(jump_to, SelectionGoal::None);
5803 s.select_anchors(vec![new_selection.clone()]);
5804 });
5805 }
5806 return;
5807 }
5808 }
5809
5810 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5811 active_diagnostics
5812 .primary_range
5813 .to_offset(&buffer)
5814 .to_inclusive()
5815 });
5816 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5817 if active_primary_range.contains(&selection.head()) {
5818 *active_primary_range.end()
5819 } else {
5820 selection.head()
5821 }
5822 } else {
5823 selection.head()
5824 };
5825
5826 loop {
5827 let mut diagnostics = if direction == Direction::Prev {
5828 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5829 } else {
5830 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5831 };
5832 let group = diagnostics.find_map(|entry| {
5833 if entry.diagnostic.is_primary
5834 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5835 && !entry.range.is_empty()
5836 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5837 {
5838 Some((entry.range, entry.diagnostic.group_id))
5839 } else {
5840 None
5841 }
5842 });
5843
5844 if let Some((primary_range, group_id)) = group {
5845 if self.activate_diagnostics(group_id, cx) {
5846 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5847 s.select(vec![Selection {
5848 id: selection.id,
5849 start: primary_range.start,
5850 end: primary_range.start,
5851 reversed: false,
5852 goal: SelectionGoal::None,
5853 }]);
5854 });
5855 }
5856 break;
5857 } else {
5858 // Cycle around to the start of the buffer, potentially moving back to the start of
5859 // the currently active diagnostic.
5860 active_primary_range.take();
5861 if direction == Direction::Prev {
5862 if search_start == buffer.len() {
5863 break;
5864 } else {
5865 search_start = buffer.len();
5866 }
5867 } else if search_start == 0 {
5868 break;
5869 } else {
5870 search_start = 0;
5871 }
5872 }
5873 }
5874 }
5875
5876 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5877 let snapshot = self
5878 .display_map
5879 .update(cx, |display_map, cx| display_map.snapshot(cx));
5880 let selection = self.selections.newest::<Point>(cx);
5881
5882 if !self.seek_in_direction(
5883 &snapshot,
5884 selection.head(),
5885 false,
5886 snapshot
5887 .buffer_snapshot
5888 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
5889 cx,
5890 ) {
5891 let wrapped_point = Point::zero();
5892 self.seek_in_direction(
5893 &snapshot,
5894 wrapped_point,
5895 true,
5896 snapshot
5897 .buffer_snapshot
5898 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
5899 cx,
5900 );
5901 }
5902 }
5903
5904 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5905 let snapshot = self
5906 .display_map
5907 .update(cx, |display_map, cx| display_map.snapshot(cx));
5908 let selection = self.selections.newest::<Point>(cx);
5909
5910 if !self.seek_in_direction(
5911 &snapshot,
5912 selection.head(),
5913 false,
5914 snapshot
5915 .buffer_snapshot
5916 .git_diff_hunks_in_range_rev(0..selection.head().row),
5917 cx,
5918 ) {
5919 let wrapped_point = snapshot.buffer_snapshot.max_point();
5920 self.seek_in_direction(
5921 &snapshot,
5922 wrapped_point,
5923 true,
5924 snapshot
5925 .buffer_snapshot
5926 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
5927 cx,
5928 );
5929 }
5930 }
5931
5932 fn seek_in_direction(
5933 &mut self,
5934 snapshot: &DisplaySnapshot,
5935 initial_point: Point,
5936 is_wrapped: bool,
5937 hunks: impl Iterator<Item = DiffHunk<u32>>,
5938 cx: &mut ViewContext<Editor>,
5939 ) -> bool {
5940 let display_point = initial_point.to_display_point(snapshot);
5941 let mut hunks = hunks
5942 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5943 .skip_while(|hunk| {
5944 if is_wrapped {
5945 false
5946 } else {
5947 hunk.contains_display_row(display_point.row())
5948 }
5949 })
5950 .dedup();
5951
5952 if let Some(hunk) = hunks.next() {
5953 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5954 let row = hunk.start_display_row();
5955 let point = DisplayPoint::new(row, 0);
5956 s.select_display_ranges([point..point]);
5957 });
5958
5959 true
5960 } else {
5961 false
5962 }
5963 }
5964
5965 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
5966 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
5967 }
5968
5969 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
5970 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
5971 }
5972
5973 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
5974 let Some(workspace) = self.workspace(cx) else { return };
5975 let buffer = self.buffer.read(cx);
5976 let head = self.selections.newest::<usize>(cx).head();
5977 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5978 text_anchor
5979 } else {
5980 return;
5981 };
5982
5983 let project = workspace.read(cx).project().clone();
5984 let definitions = project.update(cx, |project, cx| match kind {
5985 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5986 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5987 });
5988
5989 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
5990 let definitions = definitions.await?;
5991 editor.update(&mut cx, |editor, cx| {
5992 editor.navigate_to_definitions(definitions, cx);
5993 })?;
5994 Ok::<(), anyhow::Error>(())
5995 })
5996 .detach_and_log_err(cx);
5997 }
5998
5999 pub fn navigate_to_definitions(
6000 &mut self,
6001 mut definitions: Vec<LocationLink>,
6002 cx: &mut ViewContext<Editor>,
6003 ) {
6004 let Some(workspace) = self.workspace(cx) else { return };
6005 let pane = workspace.read(cx).active_pane().clone();
6006 // If there is one definition, just open it directly
6007 if definitions.len() == 1 {
6008 let definition = definitions.pop().unwrap();
6009 let range = definition
6010 .target
6011 .range
6012 .to_offset(definition.target.buffer.read(cx));
6013
6014 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6015 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6016 s.select_ranges([range]);
6017 });
6018 } else {
6019 cx.window_context().defer(move |cx| {
6020 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6021 workspace.open_project_item(definition.target.buffer.clone(), cx)
6022 });
6023 target_editor.update(cx, |target_editor, cx| {
6024 // When selecting a definition in a different buffer, disable the nav history
6025 // to avoid creating a history entry at the previous cursor location.
6026 pane.update(cx, |pane, _| pane.disable_history());
6027 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6028 s.select_ranges([range]);
6029 });
6030 pane.update(cx, |pane, _| pane.enable_history());
6031 });
6032 });
6033 }
6034 } else if !definitions.is_empty() {
6035 let replica_id = self.replica_id(cx);
6036 cx.window_context().defer(move |cx| {
6037 let title = definitions
6038 .iter()
6039 .find(|definition| definition.origin.is_some())
6040 .and_then(|definition| {
6041 definition.origin.as_ref().map(|origin| {
6042 let buffer = origin.buffer.read(cx);
6043 format!(
6044 "Definitions for {}",
6045 buffer
6046 .text_for_range(origin.range.clone())
6047 .collect::<String>()
6048 )
6049 })
6050 })
6051 .unwrap_or("Definitions".to_owned());
6052 let locations = definitions
6053 .into_iter()
6054 .map(|definition| definition.target)
6055 .collect();
6056 workspace.update(cx, |workspace, cx| {
6057 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
6058 });
6059 });
6060 }
6061 }
6062
6063 pub fn find_all_references(
6064 workspace: &mut Workspace,
6065 _: &FindAllReferences,
6066 cx: &mut ViewContext<Workspace>,
6067 ) -> Option<Task<Result<()>>> {
6068 let active_item = workspace.active_item(cx)?;
6069 let editor_handle = active_item.act_as::<Self>(cx)?;
6070
6071 let editor = editor_handle.read(cx);
6072 let buffer = editor.buffer.read(cx);
6073 let head = editor.selections.newest::<usize>(cx).head();
6074 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6075 let replica_id = editor.replica_id(cx);
6076
6077 let project = workspace.project().clone();
6078 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6079 Some(cx.spawn_labeled(
6080 "Finding All References...",
6081 |workspace, mut cx| async move {
6082 let locations = references.await?;
6083 if locations.is_empty() {
6084 return Ok(());
6085 }
6086
6087 workspace.update(&mut cx, |workspace, cx| {
6088 let title = locations
6089 .first()
6090 .as_ref()
6091 .map(|location| {
6092 let buffer = location.buffer.read(cx);
6093 format!(
6094 "References to `{}`",
6095 buffer
6096 .text_for_range(location.range.clone())
6097 .collect::<String>()
6098 )
6099 })
6100 .unwrap();
6101 Self::open_locations_in_multibuffer(
6102 workspace, locations, replica_id, title, cx,
6103 );
6104 })?;
6105
6106 Ok(())
6107 },
6108 ))
6109 }
6110
6111 /// Opens a multibuffer with the given project locations in it
6112 pub fn open_locations_in_multibuffer(
6113 workspace: &mut Workspace,
6114 mut locations: Vec<Location>,
6115 replica_id: ReplicaId,
6116 title: String,
6117 cx: &mut ViewContext<Workspace>,
6118 ) {
6119 // If there are multiple definitions, open them in a multibuffer
6120 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6121 let mut locations = locations.into_iter().peekable();
6122 let mut ranges_to_highlight = Vec::new();
6123
6124 let excerpt_buffer = cx.add_model(|cx| {
6125 let mut multibuffer = MultiBuffer::new(replica_id);
6126 while let Some(location) = locations.next() {
6127 let buffer = location.buffer.read(cx);
6128 let mut ranges_for_buffer = Vec::new();
6129 let range = location.range.to_offset(buffer);
6130 ranges_for_buffer.push(range.clone());
6131
6132 while let Some(next_location) = locations.peek() {
6133 if next_location.buffer == location.buffer {
6134 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6135 locations.next();
6136 } else {
6137 break;
6138 }
6139 }
6140
6141 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6142 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6143 location.buffer.clone(),
6144 ranges_for_buffer,
6145 1,
6146 cx,
6147 ))
6148 }
6149
6150 multibuffer.with_title(title)
6151 });
6152
6153 let editor = cx.add_view(|cx| {
6154 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6155 });
6156 editor.update(cx, |editor, cx| {
6157 editor.highlight_background::<Self>(
6158 ranges_to_highlight,
6159 |theme| theme.editor.highlighted_line_background,
6160 cx,
6161 );
6162 });
6163 workspace.add_item(Box::new(editor), cx);
6164 }
6165
6166 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6167 use language::ToOffset as _;
6168
6169 let project = self.project.clone()?;
6170 let selection = self.selections.newest_anchor().clone();
6171 let (cursor_buffer, cursor_buffer_position) = self
6172 .buffer
6173 .read(cx)
6174 .text_anchor_for_position(selection.head(), cx)?;
6175 let (tail_buffer, _) = self
6176 .buffer
6177 .read(cx)
6178 .text_anchor_for_position(selection.tail(), cx)?;
6179 if tail_buffer != cursor_buffer {
6180 return None;
6181 }
6182
6183 let snapshot = cursor_buffer.read(cx).snapshot();
6184 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6185 let prepare_rename = project.update(cx, |project, cx| {
6186 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6187 });
6188
6189 Some(cx.spawn(|this, mut cx| async move {
6190 let rename_range = if let Some(range) = prepare_rename.await? {
6191 Some(range)
6192 } else {
6193 this.read_with(&cx, |this, cx| {
6194 let buffer = this.buffer.read(cx).snapshot(cx);
6195 let mut buffer_highlights = this
6196 .document_highlights_for_position(selection.head(), &buffer)
6197 .filter(|highlight| {
6198 highlight.start.excerpt_id() == selection.head().excerpt_id()
6199 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6200 });
6201 buffer_highlights
6202 .next()
6203 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6204 })?
6205 };
6206 if let Some(rename_range) = rename_range {
6207 let rename_buffer_range = rename_range.to_offset(&snapshot);
6208 let cursor_offset_in_rename_range =
6209 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6210
6211 this.update(&mut cx, |this, cx| {
6212 this.take_rename(false, cx);
6213 let style = this.style(cx);
6214 let buffer = this.buffer.read(cx).read(cx);
6215 let cursor_offset = selection.head().to_offset(&buffer);
6216 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6217 let rename_end = rename_start + rename_buffer_range.len();
6218 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6219 let mut old_highlight_id = None;
6220 let old_name: Arc<str> = buffer
6221 .chunks(rename_start..rename_end, true)
6222 .map(|chunk| {
6223 if old_highlight_id.is_none() {
6224 old_highlight_id = chunk.syntax_highlight_id;
6225 }
6226 chunk.text
6227 })
6228 .collect::<String>()
6229 .into();
6230
6231 drop(buffer);
6232
6233 // Position the selection in the rename editor so that it matches the current selection.
6234 this.show_local_selections = false;
6235 let rename_editor = cx.add_view(|cx| {
6236 let mut editor = Editor::single_line(None, cx);
6237 if let Some(old_highlight_id) = old_highlight_id {
6238 editor.override_text_style =
6239 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6240 }
6241 editor.buffer.update(cx, |buffer, cx| {
6242 buffer.edit([(0..0, old_name.clone())], None, cx)
6243 });
6244 editor.select_all(&SelectAll, cx);
6245 editor
6246 });
6247
6248 let ranges = this
6249 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6250 .into_iter()
6251 .flat_map(|(_, ranges)| ranges)
6252 .chain(
6253 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6254 .into_iter()
6255 .flat_map(|(_, ranges)| ranges),
6256 )
6257 .collect();
6258
6259 this.highlight_text::<Rename>(
6260 ranges,
6261 HighlightStyle {
6262 fade_out: Some(style.rename_fade),
6263 ..Default::default()
6264 },
6265 cx,
6266 );
6267 cx.focus(&rename_editor);
6268 let block_id = this.insert_blocks(
6269 [BlockProperties {
6270 style: BlockStyle::Flex,
6271 position: range.start.clone(),
6272 height: 1,
6273 render: Arc::new({
6274 let editor = rename_editor.clone();
6275 move |cx: &mut BlockContext| {
6276 ChildView::new(&editor, cx)
6277 .contained()
6278 .with_padding_left(cx.anchor_x)
6279 .into_any()
6280 }
6281 }),
6282 disposition: BlockDisposition::Below,
6283 }],
6284 Some(Autoscroll::fit()),
6285 cx,
6286 )[0];
6287 this.pending_rename = Some(RenameState {
6288 range,
6289 old_name,
6290 editor: rename_editor,
6291 block_id,
6292 });
6293 })?;
6294 }
6295
6296 Ok(())
6297 }))
6298 }
6299
6300 pub fn confirm_rename(
6301 workspace: &mut Workspace,
6302 _: &ConfirmRename,
6303 cx: &mut ViewContext<Workspace>,
6304 ) -> Option<Task<Result<()>>> {
6305 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6306
6307 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6308 let rename = editor.take_rename(false, cx)?;
6309 let buffer = editor.buffer.read(cx);
6310 let (start_buffer, start) =
6311 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6312 let (end_buffer, end) =
6313 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6314 if start_buffer == end_buffer {
6315 let new_name = rename.editor.read(cx).text(cx);
6316 Some((start_buffer, start..end, rename.old_name, new_name))
6317 } else {
6318 None
6319 }
6320 })?;
6321
6322 let rename = workspace.project().clone().update(cx, |project, cx| {
6323 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6324 });
6325
6326 let editor = editor.downgrade();
6327 Some(cx.spawn(|workspace, mut cx| async move {
6328 let project_transaction = rename.await?;
6329 Self::open_project_transaction(
6330 &editor,
6331 workspace,
6332 project_transaction,
6333 format!("Rename: {} → {}", old_name, new_name),
6334 cx.clone(),
6335 )
6336 .await?;
6337
6338 editor.update(&mut cx, |editor, cx| {
6339 editor.refresh_document_highlights(cx);
6340 })?;
6341 Ok(())
6342 }))
6343 }
6344
6345 fn take_rename(
6346 &mut self,
6347 moving_cursor: bool,
6348 cx: &mut ViewContext<Self>,
6349 ) -> Option<RenameState> {
6350 let rename = self.pending_rename.take()?;
6351 self.remove_blocks(
6352 [rename.block_id].into_iter().collect(),
6353 Some(Autoscroll::fit()),
6354 cx,
6355 );
6356 self.clear_text_highlights::<Rename>(cx);
6357 self.show_local_selections = true;
6358
6359 if moving_cursor {
6360 let rename_editor = rename.editor.read(cx);
6361 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6362
6363 // Update the selection to match the position of the selection inside
6364 // the rename editor.
6365 let snapshot = self.buffer.read(cx).read(cx);
6366 let rename_range = rename.range.to_offset(&snapshot);
6367 let cursor_in_editor = snapshot
6368 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6369 .min(rename_range.end);
6370 drop(snapshot);
6371
6372 self.change_selections(None, cx, |s| {
6373 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6374 });
6375 } else {
6376 self.refresh_document_highlights(cx);
6377 }
6378
6379 Some(rename)
6380 }
6381
6382 #[cfg(any(test, feature = "test-support"))]
6383 pub fn pending_rename(&self) -> Option<&RenameState> {
6384 self.pending_rename.as_ref()
6385 }
6386
6387 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6388 let project = match &self.project {
6389 Some(project) => project.clone(),
6390 None => return None,
6391 };
6392
6393 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6394 }
6395
6396 fn perform_format(
6397 &mut self,
6398 project: ModelHandle<Project>,
6399 trigger: FormatTrigger,
6400 cx: &mut ViewContext<Self>,
6401 ) -> Task<Result<()>> {
6402 let buffer = self.buffer().clone();
6403 let buffers = buffer.read(cx).all_buffers();
6404
6405 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6406 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6407
6408 cx.spawn(|_, mut cx| async move {
6409 let transaction = futures::select_biased! {
6410 _ = timeout => {
6411 log::warn!("timed out waiting for formatting");
6412 None
6413 }
6414 transaction = format.log_err().fuse() => transaction,
6415 };
6416
6417 buffer.update(&mut cx, |buffer, cx| {
6418 if let Some(transaction) = transaction {
6419 if !buffer.is_singleton() {
6420 buffer.push_transaction(&transaction.0, cx);
6421 }
6422 }
6423
6424 cx.notify();
6425 });
6426
6427 Ok(())
6428 })
6429 }
6430
6431 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6432 if let Some(project) = self.project.clone() {
6433 self.buffer.update(cx, |multi_buffer, cx| {
6434 project.update(cx, |project, cx| {
6435 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6436 });
6437 })
6438 }
6439 }
6440
6441 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6442 cx.show_character_palette();
6443 }
6444
6445 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6446 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6447 let buffer = self.buffer.read(cx).snapshot(cx);
6448 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6449 let is_valid = buffer
6450 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6451 .any(|entry| {
6452 entry.diagnostic.is_primary
6453 && !entry.range.is_empty()
6454 && entry.range.start == primary_range_start
6455 && entry.diagnostic.message == active_diagnostics.primary_message
6456 });
6457
6458 if is_valid != active_diagnostics.is_valid {
6459 active_diagnostics.is_valid = is_valid;
6460 let mut new_styles = HashMap::default();
6461 for (block_id, diagnostic) in &active_diagnostics.blocks {
6462 new_styles.insert(
6463 *block_id,
6464 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6465 );
6466 }
6467 self.display_map
6468 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6469 }
6470 }
6471 }
6472
6473 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6474 self.dismiss_diagnostics(cx);
6475 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6476 let buffer = self.buffer.read(cx).snapshot(cx);
6477
6478 let mut primary_range = None;
6479 let mut primary_message = None;
6480 let mut group_end = Point::zero();
6481 let diagnostic_group = buffer
6482 .diagnostic_group::<Point>(group_id)
6483 .map(|entry| {
6484 if entry.range.end > group_end {
6485 group_end = entry.range.end;
6486 }
6487 if entry.diagnostic.is_primary {
6488 primary_range = Some(entry.range.clone());
6489 primary_message = Some(entry.diagnostic.message.clone());
6490 }
6491 entry
6492 })
6493 .collect::<Vec<_>>();
6494 let primary_range = primary_range?;
6495 let primary_message = primary_message?;
6496 let primary_range =
6497 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6498
6499 let blocks = display_map
6500 .insert_blocks(
6501 diagnostic_group.iter().map(|entry| {
6502 let diagnostic = entry.diagnostic.clone();
6503 let message_height = diagnostic.message.lines().count() as u8;
6504 BlockProperties {
6505 style: BlockStyle::Fixed,
6506 position: buffer.anchor_after(entry.range.start),
6507 height: message_height,
6508 render: diagnostic_block_renderer(diagnostic, true),
6509 disposition: BlockDisposition::Below,
6510 }
6511 }),
6512 cx,
6513 )
6514 .into_iter()
6515 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6516 .collect();
6517
6518 Some(ActiveDiagnosticGroup {
6519 primary_range,
6520 primary_message,
6521 blocks,
6522 is_valid: true,
6523 })
6524 });
6525 self.active_diagnostics.is_some()
6526 }
6527
6528 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6529 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6530 self.display_map.update(cx, |display_map, cx| {
6531 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6532 });
6533 cx.notify();
6534 }
6535 }
6536
6537 pub fn set_selections_from_remote(
6538 &mut self,
6539 selections: Vec<Selection<Anchor>>,
6540 pending_selection: Option<Selection<Anchor>>,
6541 cx: &mut ViewContext<Self>,
6542 ) {
6543 let old_cursor_position = self.selections.newest_anchor().head();
6544 self.selections.change_with(cx, |s| {
6545 s.select_anchors(selections);
6546 if let Some(pending_selection) = pending_selection {
6547 s.set_pending(pending_selection, SelectMode::Character);
6548 } else {
6549 s.clear_pending();
6550 }
6551 });
6552 self.selections_did_change(false, &old_cursor_position, cx);
6553 }
6554
6555 fn push_to_selection_history(&mut self) {
6556 self.selection_history.push(SelectionHistoryEntry {
6557 selections: self.selections.disjoint_anchors(),
6558 select_next_state: self.select_next_state.clone(),
6559 select_prev_state: self.select_prev_state.clone(),
6560 add_selections_state: self.add_selections_state.clone(),
6561 });
6562 }
6563
6564 pub fn transact(
6565 &mut self,
6566 cx: &mut ViewContext<Self>,
6567 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6568 ) -> Option<TransactionId> {
6569 self.start_transaction_at(Instant::now(), cx);
6570 update(self, cx);
6571 self.end_transaction_at(Instant::now(), cx)
6572 }
6573
6574 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6575 self.end_selection(cx);
6576 if let Some(tx_id) = self
6577 .buffer
6578 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6579 {
6580 self.selection_history
6581 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6582 }
6583 }
6584
6585 fn end_transaction_at(
6586 &mut self,
6587 now: Instant,
6588 cx: &mut ViewContext<Self>,
6589 ) -> Option<TransactionId> {
6590 if let Some(tx_id) = self
6591 .buffer
6592 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6593 {
6594 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6595 *end_selections = Some(self.selections.disjoint_anchors());
6596 } else {
6597 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6598 }
6599
6600 cx.emit(Event::Edited);
6601 Some(tx_id)
6602 } else {
6603 None
6604 }
6605 }
6606
6607 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6608 let mut fold_ranges = Vec::new();
6609
6610 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6611
6612 let selections = self.selections.all::<Point>(cx);
6613 for selection in selections {
6614 let range = selection.range().sorted();
6615 let buffer_start_row = range.start.row;
6616
6617 for row in (0..=range.end.row).rev() {
6618 let fold_range = display_map.foldable_range(row);
6619
6620 if let Some(fold_range) = fold_range {
6621 if fold_range.end.row >= buffer_start_row {
6622 fold_ranges.push(fold_range);
6623 if row <= range.start.row {
6624 break;
6625 }
6626 }
6627 }
6628 }
6629 }
6630
6631 self.fold_ranges(fold_ranges, true, cx);
6632 }
6633
6634 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6635 let buffer_row = fold_at.buffer_row;
6636 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6637
6638 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6639 let autoscroll = self
6640 .selections
6641 .all::<Point>(cx)
6642 .iter()
6643 .any(|selection| fold_range.overlaps(&selection.range()));
6644
6645 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6646 }
6647 }
6648
6649 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6650 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6651 let buffer = &display_map.buffer_snapshot;
6652 let selections = self.selections.all::<Point>(cx);
6653 let ranges = selections
6654 .iter()
6655 .map(|s| {
6656 let range = s.display_range(&display_map).sorted();
6657 let mut start = range.start.to_point(&display_map);
6658 let mut end = range.end.to_point(&display_map);
6659 start.column = 0;
6660 end.column = buffer.line_len(end.row);
6661 start..end
6662 })
6663 .collect::<Vec<_>>();
6664
6665 self.unfold_ranges(ranges, true, true, cx);
6666 }
6667
6668 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6669 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6670
6671 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6672 ..Point::new(
6673 unfold_at.buffer_row,
6674 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6675 );
6676
6677 let autoscroll = self
6678 .selections
6679 .all::<Point>(cx)
6680 .iter()
6681 .any(|selection| selection.range().overlaps(&intersection_range));
6682
6683 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6684 }
6685
6686 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6687 let selections = self.selections.all::<Point>(cx);
6688 let ranges = selections.into_iter().map(|s| s.start..s.end);
6689 self.fold_ranges(ranges, true, cx);
6690 }
6691
6692 pub fn fold_ranges<T: ToOffset + Clone>(
6693 &mut self,
6694 ranges: impl IntoIterator<Item = Range<T>>,
6695 auto_scroll: bool,
6696 cx: &mut ViewContext<Self>,
6697 ) {
6698 let mut ranges = ranges.into_iter().peekable();
6699 if ranges.peek().is_some() {
6700 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6701
6702 if auto_scroll {
6703 self.request_autoscroll(Autoscroll::fit(), cx);
6704 }
6705
6706 cx.notify();
6707 }
6708 }
6709
6710 pub fn unfold_ranges<T: ToOffset + Clone>(
6711 &mut self,
6712 ranges: impl IntoIterator<Item = Range<T>>,
6713 inclusive: bool,
6714 auto_scroll: bool,
6715 cx: &mut ViewContext<Self>,
6716 ) {
6717 let mut ranges = ranges.into_iter().peekable();
6718 if ranges.peek().is_some() {
6719 self.display_map
6720 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6721 if auto_scroll {
6722 self.request_autoscroll(Autoscroll::fit(), cx);
6723 }
6724
6725 cx.notify();
6726 }
6727 }
6728
6729 pub fn gutter_hover(
6730 &mut self,
6731 GutterHover { hovered }: &GutterHover,
6732 cx: &mut ViewContext<Self>,
6733 ) {
6734 self.gutter_hovered = *hovered;
6735 cx.notify();
6736 }
6737
6738 pub fn insert_blocks(
6739 &mut self,
6740 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6741 autoscroll: Option<Autoscroll>,
6742 cx: &mut ViewContext<Self>,
6743 ) -> Vec<BlockId> {
6744 let blocks = self
6745 .display_map
6746 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6747 if let Some(autoscroll) = autoscroll {
6748 self.request_autoscroll(autoscroll, cx);
6749 }
6750 blocks
6751 }
6752
6753 pub fn replace_blocks(
6754 &mut self,
6755 blocks: HashMap<BlockId, RenderBlock>,
6756 autoscroll: Option<Autoscroll>,
6757 cx: &mut ViewContext<Self>,
6758 ) {
6759 self.display_map
6760 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6761 if let Some(autoscroll) = autoscroll {
6762 self.request_autoscroll(autoscroll, cx);
6763 }
6764 }
6765
6766 pub fn remove_blocks(
6767 &mut self,
6768 block_ids: HashSet<BlockId>,
6769 autoscroll: Option<Autoscroll>,
6770 cx: &mut ViewContext<Self>,
6771 ) {
6772 self.display_map.update(cx, |display_map, cx| {
6773 display_map.remove_blocks(block_ids, cx)
6774 });
6775 if let Some(autoscroll) = autoscroll {
6776 self.request_autoscroll(autoscroll, cx);
6777 }
6778 }
6779
6780 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6781 self.display_map
6782 .update(cx, |map, cx| map.snapshot(cx))
6783 .longest_row()
6784 }
6785
6786 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6787 self.display_map
6788 .update(cx, |map, cx| map.snapshot(cx))
6789 .max_point()
6790 }
6791
6792 pub fn text(&self, cx: &AppContext) -> String {
6793 self.buffer.read(cx).read(cx).text()
6794 }
6795
6796 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6797 self.transact(cx, |this, cx| {
6798 this.buffer
6799 .read(cx)
6800 .as_singleton()
6801 .expect("you can only call set_text on editors for singleton buffers")
6802 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6803 });
6804 }
6805
6806 pub fn display_text(&self, cx: &mut AppContext) -> String {
6807 self.display_map
6808 .update(cx, |map, cx| map.snapshot(cx))
6809 .text()
6810 }
6811
6812 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6813 let settings = self.buffer.read(cx).settings_at(0, cx);
6814 let mode = self
6815 .soft_wrap_mode_override
6816 .unwrap_or_else(|| settings.soft_wrap);
6817 match mode {
6818 language_settings::SoftWrap::None => SoftWrap::None,
6819 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6820 language_settings::SoftWrap::PreferredLineLength => {
6821 SoftWrap::Column(settings.preferred_line_length)
6822 }
6823 }
6824 }
6825
6826 pub fn set_soft_wrap_mode(
6827 &mut self,
6828 mode: language_settings::SoftWrap,
6829 cx: &mut ViewContext<Self>,
6830 ) {
6831 self.soft_wrap_mode_override = Some(mode);
6832 cx.notify();
6833 }
6834
6835 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6836 self.display_map
6837 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6838 }
6839
6840 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6841 if self.soft_wrap_mode_override.is_some() {
6842 self.soft_wrap_mode_override.take();
6843 } else {
6844 let soft_wrap = match self.soft_wrap_mode(cx) {
6845 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
6846 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
6847 };
6848 self.soft_wrap_mode_override = Some(soft_wrap);
6849 }
6850 cx.notify();
6851 }
6852
6853 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
6854 self.show_gutter = show_gutter;
6855 cx.notify();
6856 }
6857
6858 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6859 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6860 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6861 cx.reveal_path(&file.abs_path(cx));
6862 }
6863 }
6864 }
6865
6866 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6867 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6868 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6869 if let Some(path) = file.abs_path(cx).to_str() {
6870 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6871 }
6872 }
6873 }
6874 }
6875
6876 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6877 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6878 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6879 if let Some(path) = file.path().to_str() {
6880 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6881 }
6882 }
6883 }
6884 }
6885
6886 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6887 self.highlighted_rows = rows;
6888 }
6889
6890 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6891 self.highlighted_rows.clone()
6892 }
6893
6894 pub fn highlight_background<T: 'static>(
6895 &mut self,
6896 ranges: Vec<Range<Anchor>>,
6897 color_fetcher: fn(&Theme) -> Color,
6898 cx: &mut ViewContext<Self>,
6899 ) {
6900 self.background_highlights
6901 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6902 cx.notify();
6903 }
6904
6905 #[allow(clippy::type_complexity)]
6906 pub fn clear_background_highlights<T: 'static>(
6907 &mut self,
6908 cx: &mut ViewContext<Self>,
6909 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6910 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6911 if highlights.is_some() {
6912 cx.notify();
6913 }
6914 highlights
6915 }
6916
6917 #[cfg(feature = "test-support")]
6918 pub fn all_background_highlights(
6919 &mut self,
6920 cx: &mut ViewContext<Self>,
6921 ) -> Vec<(Range<DisplayPoint>, Color)> {
6922 let snapshot = self.snapshot(cx);
6923 let buffer = &snapshot.buffer_snapshot;
6924 let start = buffer.anchor_before(0);
6925 let end = buffer.anchor_after(buffer.len());
6926 let theme = theme::current(cx);
6927 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
6928 }
6929
6930 fn document_highlights_for_position<'a>(
6931 &'a self,
6932 position: Anchor,
6933 buffer: &'a MultiBufferSnapshot,
6934 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6935 let read_highlights = self
6936 .background_highlights
6937 .get(&TypeId::of::<DocumentHighlightRead>())
6938 .map(|h| &h.1);
6939 let write_highlights = self
6940 .background_highlights
6941 .get(&TypeId::of::<DocumentHighlightWrite>())
6942 .map(|h| &h.1);
6943 let left_position = position.bias_left(buffer);
6944 let right_position = position.bias_right(buffer);
6945 read_highlights
6946 .into_iter()
6947 .chain(write_highlights)
6948 .flat_map(move |ranges| {
6949 let start_ix = match ranges.binary_search_by(|probe| {
6950 let cmp = probe.end.cmp(&left_position, buffer);
6951 if cmp.is_ge() {
6952 Ordering::Greater
6953 } else {
6954 Ordering::Less
6955 }
6956 }) {
6957 Ok(i) | Err(i) => i,
6958 };
6959
6960 let right_position = right_position.clone();
6961 ranges[start_ix..]
6962 .iter()
6963 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6964 })
6965 }
6966
6967 pub fn background_highlights_in_range(
6968 &self,
6969 search_range: Range<Anchor>,
6970 display_snapshot: &DisplaySnapshot,
6971 theme: &Theme,
6972 ) -> Vec<(Range<DisplayPoint>, Color)> {
6973 let mut results = Vec::new();
6974 let buffer = &display_snapshot.buffer_snapshot;
6975 for (color_fetcher, ranges) in self.background_highlights.values() {
6976 let color = color_fetcher(theme);
6977 let start_ix = match ranges.binary_search_by(|probe| {
6978 let cmp = probe.end.cmp(&search_range.start, buffer);
6979 if cmp.is_gt() {
6980 Ordering::Greater
6981 } else {
6982 Ordering::Less
6983 }
6984 }) {
6985 Ok(i) | Err(i) => i,
6986 };
6987 for range in &ranges[start_ix..] {
6988 if range.start.cmp(&search_range.end, buffer).is_ge() {
6989 break;
6990 }
6991 let start = range
6992 .start
6993 .to_point(buffer)
6994 .to_display_point(display_snapshot);
6995 let end = range
6996 .end
6997 .to_point(buffer)
6998 .to_display_point(display_snapshot);
6999 results.push((start..end, color))
7000 }
7001 }
7002 results
7003 }
7004
7005 pub fn highlight_text<T: 'static>(
7006 &mut self,
7007 ranges: Vec<Range<Anchor>>,
7008 style: HighlightStyle,
7009 cx: &mut ViewContext<Self>,
7010 ) {
7011 self.display_map.update(cx, |map, _| {
7012 map.highlight_text(TypeId::of::<T>(), ranges, style)
7013 });
7014 cx.notify();
7015 }
7016
7017 pub fn text_highlights<'a, T: 'static>(
7018 &'a self,
7019 cx: &'a AppContext,
7020 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7021 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7022 }
7023
7024 pub fn clear_text_highlights<T: 'static>(
7025 &mut self,
7026 cx: &mut ViewContext<Self>,
7027 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7028 let highlights = self
7029 .display_map
7030 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7031 if highlights.is_some() {
7032 cx.notify();
7033 }
7034 highlights
7035 }
7036
7037 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7038 self.blink_manager.read(cx).visible() && self.focused
7039 }
7040
7041 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7042 cx.notify();
7043 }
7044
7045 fn on_buffer_event(
7046 &mut self,
7047 _: ModelHandle<MultiBuffer>,
7048 event: &multi_buffer::Event,
7049 cx: &mut ViewContext<Self>,
7050 ) {
7051 match event {
7052 multi_buffer::Event::Edited => {
7053 self.refresh_active_diagnostics(cx);
7054 self.refresh_code_actions(cx);
7055 if self.has_active_copilot_suggestion(cx) {
7056 self.update_visible_copilot_suggestion(cx);
7057 }
7058 cx.emit(Event::BufferEdited);
7059 }
7060 multi_buffer::Event::ExcerptsAdded {
7061 buffer,
7062 predecessor,
7063 excerpts,
7064 } => cx.emit(Event::ExcerptsAdded {
7065 buffer: buffer.clone(),
7066 predecessor: *predecessor,
7067 excerpts: excerpts.clone(),
7068 }),
7069 multi_buffer::Event::ExcerptsRemoved { ids } => {
7070 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7071 }
7072 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7073 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7074 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7075 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7076 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7077 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7078 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7079 multi_buffer::Event::DiagnosticsUpdated => {
7080 self.refresh_active_diagnostics(cx);
7081 }
7082 _ => {}
7083 }
7084 }
7085
7086 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7087 cx.notify();
7088 }
7089
7090 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7091 self.refresh_copilot_suggestions(true, cx);
7092 }
7093
7094 pub fn set_searchable(&mut self, searchable: bool) {
7095 self.searchable = searchable;
7096 }
7097
7098 pub fn searchable(&self) -> bool {
7099 self.searchable
7100 }
7101
7102 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7103 let active_item = workspace.active_item(cx);
7104 let editor_handle = if let Some(editor) = active_item
7105 .as_ref()
7106 .and_then(|item| item.act_as::<Self>(cx))
7107 {
7108 editor
7109 } else {
7110 cx.propagate_action();
7111 return;
7112 };
7113
7114 let editor = editor_handle.read(cx);
7115 let buffer = editor.buffer.read(cx);
7116 if buffer.is_singleton() {
7117 cx.propagate_action();
7118 return;
7119 }
7120
7121 let mut new_selections_by_buffer = HashMap::default();
7122 for selection in editor.selections.all::<usize>(cx) {
7123 for (buffer, mut range, _) in
7124 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7125 {
7126 if selection.reversed {
7127 mem::swap(&mut range.start, &mut range.end);
7128 }
7129 new_selections_by_buffer
7130 .entry(buffer)
7131 .or_insert(Vec::new())
7132 .push(range)
7133 }
7134 }
7135
7136 editor_handle.update(cx, |editor, cx| {
7137 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7138 });
7139 let pane = workspace.active_pane().clone();
7140 pane.update(cx, |pane, _| pane.disable_history());
7141
7142 // We defer the pane interaction because we ourselves are a workspace item
7143 // and activating a new item causes the pane to call a method on us reentrantly,
7144 // which panics if we're on the stack.
7145 cx.defer(move |workspace, cx| {
7146 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7147 let editor = workspace.open_project_item::<Self>(buffer, cx);
7148 editor.update(cx, |editor, cx| {
7149 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7150 s.select_ranges(ranges);
7151 });
7152 });
7153 }
7154
7155 pane.update(cx, |pane, _| pane.enable_history());
7156 });
7157 }
7158
7159 fn jump(
7160 workspace: &mut Workspace,
7161 path: ProjectPath,
7162 position: Point,
7163 anchor: language::Anchor,
7164 cx: &mut ViewContext<Workspace>,
7165 ) {
7166 let editor = workspace.open_path(path, None, true, cx);
7167 cx.spawn(|_, mut cx| async move {
7168 let editor = editor
7169 .await?
7170 .downcast::<Editor>()
7171 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7172 .downgrade();
7173 editor.update(&mut cx, |editor, cx| {
7174 let buffer = editor
7175 .buffer()
7176 .read(cx)
7177 .as_singleton()
7178 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7179 let buffer = buffer.read(cx);
7180 let cursor = if buffer.can_resolve(&anchor) {
7181 language::ToPoint::to_point(&anchor, buffer)
7182 } else {
7183 buffer.clip_point(position, Bias::Left)
7184 };
7185
7186 let nav_history = editor.nav_history.take();
7187 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7188 s.select_ranges([cursor..cursor]);
7189 });
7190 editor.nav_history = nav_history;
7191
7192 anyhow::Ok(())
7193 })??;
7194
7195 anyhow::Ok(())
7196 })
7197 .detach_and_log_err(cx);
7198 }
7199
7200 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7201 let snapshot = self.buffer.read(cx).read(cx);
7202 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7203 Some(
7204 ranges
7205 .iter()
7206 .map(move |range| {
7207 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7208 })
7209 .collect(),
7210 )
7211 }
7212
7213 fn selection_replacement_ranges(
7214 &self,
7215 range: Range<OffsetUtf16>,
7216 cx: &AppContext,
7217 ) -> Vec<Range<OffsetUtf16>> {
7218 let selections = self.selections.all::<OffsetUtf16>(cx);
7219 let newest_selection = selections
7220 .iter()
7221 .max_by_key(|selection| selection.id)
7222 .unwrap();
7223 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7224 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7225 let snapshot = self.buffer.read(cx).read(cx);
7226 selections
7227 .into_iter()
7228 .map(|mut selection| {
7229 selection.start.0 =
7230 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7231 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7232 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7233 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7234 })
7235 .collect()
7236 }
7237
7238 fn report_copilot_event(
7239 &self,
7240 suggestion_id: Option<String>,
7241 suggestion_accepted: bool,
7242 cx: &AppContext,
7243 ) {
7244 let Some(project) = &self.project else {
7245 return
7246 };
7247
7248 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7249 let file_extension = self
7250 .buffer
7251 .read(cx)
7252 .as_singleton()
7253 .and_then(|b| b.read(cx).file())
7254 .and_then(|file| Path::new(file.file_name(cx)).extension())
7255 .and_then(|e| e.to_str())
7256 .map(|a| a.to_string());
7257
7258 let telemetry = project.read(cx).client().telemetry().clone();
7259 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7260
7261 let event = ClickhouseEvent::Copilot {
7262 suggestion_id,
7263 suggestion_accepted,
7264 file_extension,
7265 };
7266 telemetry.report_clickhouse_event(event, telemetry_settings);
7267 }
7268
7269 fn report_editor_event(
7270 &self,
7271 name: &'static str,
7272 file_extension: Option<String>,
7273 cx: &AppContext,
7274 ) {
7275 let Some(project) = &self.project else {
7276 return
7277 };
7278
7279 // If None, we are in a file without an extension
7280 let file = self
7281 .buffer
7282 .read(cx)
7283 .as_singleton()
7284 .and_then(|b| b.read(cx).file());
7285 let file_extension = file_extension.or(file
7286 .as_ref()
7287 .and_then(|file| Path::new(file.file_name(cx)).extension())
7288 .and_then(|e| e.to_str())
7289 .map(|a| a.to_string()));
7290
7291 let vim_mode = cx
7292 .global::<SettingsStore>()
7293 .raw_user_settings()
7294 .get("vim_mode")
7295 == Some(&serde_json::Value::Bool(true));
7296 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7297 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7298 let copilot_enabled_for_language = self
7299 .buffer
7300 .read(cx)
7301 .settings_at(0, cx)
7302 .show_copilot_suggestions;
7303
7304 let telemetry = project.read(cx).client().telemetry().clone();
7305 let event = ClickhouseEvent::Editor {
7306 file_extension,
7307 vim_mode,
7308 operation: name,
7309 copilot_enabled,
7310 copilot_enabled_for_language,
7311 };
7312 telemetry.report_clickhouse_event(event, telemetry_settings)
7313 }
7314
7315 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7316 /// with each line being an array of {text, highlight} objects.
7317 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7318 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7319 return;
7320 };
7321
7322 #[derive(Serialize)]
7323 struct Chunk<'a> {
7324 text: String,
7325 highlight: Option<&'a str>,
7326 }
7327
7328 let snapshot = buffer.read(cx).snapshot();
7329 let range = self
7330 .selected_text_range(cx)
7331 .and_then(|selected_range| {
7332 if selected_range.is_empty() {
7333 None
7334 } else {
7335 Some(selected_range)
7336 }
7337 })
7338 .unwrap_or_else(|| 0..snapshot.len());
7339
7340 let chunks = snapshot.chunks(range, true);
7341 let mut lines = Vec::new();
7342 let mut line: VecDeque<Chunk> = VecDeque::new();
7343
7344 let theme = &theme::current(cx).editor.syntax;
7345
7346 for chunk in chunks {
7347 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7348 let mut chunk_lines = chunk.text.split("\n").peekable();
7349 while let Some(text) = chunk_lines.next() {
7350 let mut merged_with_last_token = false;
7351 if let Some(last_token) = line.back_mut() {
7352 if last_token.highlight == highlight {
7353 last_token.text.push_str(text);
7354 merged_with_last_token = true;
7355 }
7356 }
7357
7358 if !merged_with_last_token {
7359 line.push_back(Chunk {
7360 text: text.into(),
7361 highlight,
7362 });
7363 }
7364
7365 if chunk_lines.peek().is_some() {
7366 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7367 line.pop_front();
7368 }
7369 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7370 line.pop_back();
7371 }
7372
7373 lines.push(mem::take(&mut line));
7374 }
7375 }
7376 }
7377
7378 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7379 cx.write_to_clipboard(ClipboardItem::new(lines));
7380 }
7381}
7382
7383fn consume_contiguous_rows(
7384 contiguous_row_selections: &mut Vec<Selection<Point>>,
7385 selection: &Selection<Point>,
7386 display_map: &DisplaySnapshot,
7387 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7388) -> (u32, u32) {
7389 contiguous_row_selections.push(selection.clone());
7390 let start_row = selection.start.row;
7391 let mut end_row = ending_row(selection, display_map);
7392
7393 while let Some(next_selection) = selections.peek() {
7394 if next_selection.start.row <= end_row {
7395 end_row = ending_row(next_selection, display_map);
7396 contiguous_row_selections.push(selections.next().unwrap().clone());
7397 } else {
7398 break;
7399 }
7400 }
7401 (start_row, end_row)
7402}
7403
7404fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7405 if next_selection.end.column > 0 || next_selection.is_empty() {
7406 display_map.next_line_boundary(next_selection.end).0.row + 1
7407 } else {
7408 next_selection.end.row
7409 }
7410}
7411
7412impl EditorSnapshot {
7413 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7414 self.display_snapshot.buffer_snapshot.language_at(position)
7415 }
7416
7417 pub fn is_focused(&self) -> bool {
7418 self.is_focused
7419 }
7420
7421 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7422 self.placeholder_text.as_ref()
7423 }
7424
7425 pub fn scroll_position(&self) -> Vector2F {
7426 self.scroll_anchor.scroll_position(&self.display_snapshot)
7427 }
7428}
7429
7430impl Deref for EditorSnapshot {
7431 type Target = DisplaySnapshot;
7432
7433 fn deref(&self) -> &Self::Target {
7434 &self.display_snapshot
7435 }
7436}
7437
7438#[derive(Clone, Debug, PartialEq, Eq)]
7439pub enum Event {
7440 InputIgnored {
7441 text: Arc<str>,
7442 },
7443 ExcerptsAdded {
7444 buffer: ModelHandle<Buffer>,
7445 predecessor: ExcerptId,
7446 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7447 },
7448 ExcerptsRemoved {
7449 ids: Vec<ExcerptId>,
7450 },
7451 BufferEdited,
7452 Edited,
7453 Reparsed,
7454 Focused,
7455 Blurred,
7456 DirtyChanged,
7457 Saved,
7458 TitleChanged,
7459 DiffBaseChanged,
7460 SelectionsChanged {
7461 local: bool,
7462 },
7463 ScrollPositionChanged {
7464 local: bool,
7465 autoscroll: bool,
7466 },
7467 Closed,
7468}
7469
7470pub struct EditorFocused(pub ViewHandle<Editor>);
7471pub struct EditorBlurred(pub ViewHandle<Editor>);
7472pub struct EditorReleased(pub WeakViewHandle<Editor>);
7473
7474impl Entity for Editor {
7475 type Event = Event;
7476
7477 fn release(&mut self, cx: &mut AppContext) {
7478 cx.emit_global(EditorReleased(self.handle.clone()));
7479 }
7480}
7481
7482impl View for Editor {
7483 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7484 let style = self.style(cx);
7485 let font_changed = self.display_map.update(cx, |map, cx| {
7486 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7487 map.set_font(style.text.font_id, style.text.font_size, cx)
7488 });
7489
7490 if font_changed {
7491 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7492 hide_hover(editor, cx);
7493 hide_link_definition(editor, cx);
7494 });
7495 }
7496
7497 Stack::new()
7498 .with_child(EditorElement::new(style.clone()))
7499 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7500 .into_any()
7501 }
7502
7503 fn ui_name() -> &'static str {
7504 "Editor"
7505 }
7506
7507 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7508 if cx.is_self_focused() {
7509 let focused_event = EditorFocused(cx.handle());
7510 cx.emit(Event::Focused);
7511 cx.emit_global(focused_event);
7512 }
7513 if let Some(rename) = self.pending_rename.as_ref() {
7514 cx.focus(&rename.editor);
7515 } else {
7516 if !self.focused {
7517 self.blink_manager.update(cx, BlinkManager::enable);
7518 }
7519 self.focused = true;
7520 self.buffer.update(cx, |buffer, cx| {
7521 buffer.finalize_last_transaction(cx);
7522 if self.leader_replica_id.is_none() {
7523 buffer.set_active_selections(
7524 &self.selections.disjoint_anchors(),
7525 self.selections.line_mode,
7526 self.cursor_shape,
7527 cx,
7528 );
7529 }
7530 });
7531 }
7532 }
7533
7534 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7535 let blurred_event = EditorBlurred(cx.handle());
7536 cx.emit_global(blurred_event);
7537 self.focused = false;
7538 self.blink_manager.update(cx, BlinkManager::disable);
7539 self.buffer
7540 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7541 self.hide_context_menu(cx);
7542 hide_hover(self, cx);
7543 cx.emit(Event::Blurred);
7544 cx.notify();
7545 }
7546
7547 fn modifiers_changed(
7548 &mut self,
7549 event: &gpui::platform::ModifiersChangedEvent,
7550 cx: &mut ViewContext<Self>,
7551 ) -> bool {
7552 let pending_selection = self.has_pending_selection();
7553
7554 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7555 if event.cmd && !pending_selection {
7556 let snapshot = self.snapshot(cx);
7557 let kind = if event.shift {
7558 LinkDefinitionKind::Type
7559 } else {
7560 LinkDefinitionKind::Symbol
7561 };
7562
7563 show_link_definition(kind, self, point, snapshot, cx);
7564 return false;
7565 }
7566 }
7567
7568 {
7569 if self.link_go_to_definition_state.symbol_range.is_some()
7570 || !self.link_go_to_definition_state.definitions.is_empty()
7571 {
7572 self.link_go_to_definition_state.symbol_range.take();
7573 self.link_go_to_definition_state.definitions.clear();
7574 cx.notify();
7575 }
7576
7577 self.link_go_to_definition_state.task = None;
7578
7579 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7580 }
7581
7582 false
7583 }
7584
7585 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7586 Self::reset_to_default_keymap_context(keymap);
7587 let mode = match self.mode {
7588 EditorMode::SingleLine => "single_line",
7589 EditorMode::AutoHeight { .. } => "auto_height",
7590 EditorMode::Full => "full",
7591 };
7592 keymap.add_key("mode", mode);
7593 if self.pending_rename.is_some() {
7594 keymap.add_identifier("renaming");
7595 }
7596 match self.context_menu.as_ref() {
7597 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7598 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7599 None => {}
7600 }
7601 for layer in self.keymap_context_layers.values() {
7602 keymap.extend(layer);
7603 }
7604
7605 if let Some(extension) = self
7606 .buffer
7607 .read(cx)
7608 .as_singleton()
7609 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7610 {
7611 keymap.add_key("extension", extension.to_string());
7612 }
7613 }
7614
7615 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7616 Some(
7617 self.buffer
7618 .read(cx)
7619 .read(cx)
7620 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7621 .collect(),
7622 )
7623 }
7624
7625 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7626 // Prevent the IME menu from appearing when holding down an alphabetic key
7627 // while input is disabled.
7628 if !self.input_enabled {
7629 return None;
7630 }
7631
7632 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7633 Some(range.start.0..range.end.0)
7634 }
7635
7636 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7637 let snapshot = self.buffer.read(cx).read(cx);
7638 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7639 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7640 }
7641
7642 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7643 self.clear_text_highlights::<InputComposition>(cx);
7644 self.ime_transaction.take();
7645 }
7646
7647 fn replace_text_in_range(
7648 &mut self,
7649 range_utf16: Option<Range<usize>>,
7650 text: &str,
7651 cx: &mut ViewContext<Self>,
7652 ) {
7653 self.transact(cx, |this, cx| {
7654 if this.input_enabled {
7655 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7656 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7657 Some(this.selection_replacement_ranges(range_utf16, cx))
7658 } else {
7659 this.marked_text_ranges(cx)
7660 };
7661
7662 if let Some(new_selected_ranges) = new_selected_ranges {
7663 this.change_selections(None, cx, |selections| {
7664 selections.select_ranges(new_selected_ranges)
7665 });
7666 }
7667 }
7668
7669 this.handle_input(text, cx);
7670 });
7671
7672 if !self.input_enabled {
7673 return;
7674 }
7675
7676 if let Some(transaction) = self.ime_transaction {
7677 self.buffer.update(cx, |buffer, cx| {
7678 buffer.group_until_transaction(transaction, cx);
7679 });
7680 }
7681
7682 self.unmark_text(cx);
7683 }
7684
7685 fn replace_and_mark_text_in_range(
7686 &mut self,
7687 range_utf16: Option<Range<usize>>,
7688 text: &str,
7689 new_selected_range_utf16: Option<Range<usize>>,
7690 cx: &mut ViewContext<Self>,
7691 ) {
7692 if !self.input_enabled {
7693 return;
7694 }
7695
7696 let transaction = self.transact(cx, |this, cx| {
7697 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7698 let snapshot = this.buffer.read(cx).read(cx);
7699 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7700 for marked_range in &mut marked_ranges {
7701 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7702 marked_range.start.0 += relative_range_utf16.start;
7703 marked_range.start =
7704 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7705 marked_range.end =
7706 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7707 }
7708 }
7709 Some(marked_ranges)
7710 } else if let Some(range_utf16) = range_utf16 {
7711 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7712 Some(this.selection_replacement_ranges(range_utf16, cx))
7713 } else {
7714 None
7715 };
7716
7717 if let Some(ranges) = ranges_to_replace {
7718 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7719 }
7720
7721 let marked_ranges = {
7722 let snapshot = this.buffer.read(cx).read(cx);
7723 this.selections
7724 .disjoint_anchors()
7725 .iter()
7726 .map(|selection| {
7727 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7728 })
7729 .collect::<Vec<_>>()
7730 };
7731
7732 if text.is_empty() {
7733 this.unmark_text(cx);
7734 } else {
7735 this.highlight_text::<InputComposition>(
7736 marked_ranges.clone(),
7737 this.style(cx).composition_mark,
7738 cx,
7739 );
7740 }
7741
7742 this.handle_input(text, cx);
7743
7744 if let Some(new_selected_range) = new_selected_range_utf16 {
7745 let snapshot = this.buffer.read(cx).read(cx);
7746 let new_selected_ranges = marked_ranges
7747 .into_iter()
7748 .map(|marked_range| {
7749 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7750 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7751 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7752 snapshot.clip_offset_utf16(new_start, Bias::Left)
7753 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7754 })
7755 .collect::<Vec<_>>();
7756
7757 drop(snapshot);
7758 this.change_selections(None, cx, |selections| {
7759 selections.select_ranges(new_selected_ranges)
7760 });
7761 }
7762 });
7763
7764 self.ime_transaction = self.ime_transaction.or(transaction);
7765 if let Some(transaction) = self.ime_transaction {
7766 self.buffer.update(cx, |buffer, cx| {
7767 buffer.group_until_transaction(transaction, cx);
7768 });
7769 }
7770
7771 if self.text_highlights::<InputComposition>(cx).is_none() {
7772 self.ime_transaction.take();
7773 }
7774 }
7775}
7776
7777fn build_style(
7778 settings: &ThemeSettings,
7779 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7780 override_text_style: Option<&OverrideTextStyle>,
7781 cx: &AppContext,
7782) -> EditorStyle {
7783 let font_cache = cx.font_cache();
7784
7785 let theme_id = settings.theme.meta.id;
7786 let mut theme = settings.theme.editor.clone();
7787 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7788 let field_editor_theme = get_field_editor_theme(&settings.theme);
7789 theme.text_color = field_editor_theme.text.color;
7790 theme.selection = field_editor_theme.selection;
7791 theme.background = field_editor_theme
7792 .container
7793 .background_color
7794 .unwrap_or_default();
7795 EditorStyle {
7796 text: field_editor_theme.text,
7797 placeholder_text: field_editor_theme.placeholder_text,
7798 theme,
7799 theme_id,
7800 }
7801 } else {
7802 let font_family_id = settings.buffer_font_family;
7803 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7804 let font_properties = Default::default();
7805 let font_id = font_cache
7806 .select_font(font_family_id, &font_properties)
7807 .unwrap();
7808 let font_size = settings.buffer_font_size(cx);
7809 EditorStyle {
7810 text: TextStyle {
7811 color: settings.theme.editor.text_color,
7812 font_family_name,
7813 font_family_id,
7814 font_id,
7815 font_size,
7816 font_properties,
7817 underline: Default::default(),
7818 },
7819 placeholder_text: None,
7820 theme,
7821 theme_id,
7822 }
7823 };
7824
7825 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7826 if let Some(highlighted) = style
7827 .text
7828 .clone()
7829 .highlight(highlight_style, font_cache)
7830 .log_err()
7831 {
7832 style.text = highlighted;
7833 }
7834 }
7835
7836 style
7837}
7838
7839trait SelectionExt {
7840 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7841 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7842 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7843 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7844 -> Range<u32>;
7845}
7846
7847impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7848 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7849 let start = self.start.to_point(buffer);
7850 let end = self.end.to_point(buffer);
7851 if self.reversed {
7852 end..start
7853 } else {
7854 start..end
7855 }
7856 }
7857
7858 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7859 let start = self.start.to_offset(buffer);
7860 let end = self.end.to_offset(buffer);
7861 if self.reversed {
7862 end..start
7863 } else {
7864 start..end
7865 }
7866 }
7867
7868 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7869 let start = self
7870 .start
7871 .to_point(&map.buffer_snapshot)
7872 .to_display_point(map);
7873 let end = self
7874 .end
7875 .to_point(&map.buffer_snapshot)
7876 .to_display_point(map);
7877 if self.reversed {
7878 end..start
7879 } else {
7880 start..end
7881 }
7882 }
7883
7884 fn spanned_rows(
7885 &self,
7886 include_end_if_at_line_start: bool,
7887 map: &DisplaySnapshot,
7888 ) -> Range<u32> {
7889 let start = self.start.to_point(&map.buffer_snapshot);
7890 let mut end = self.end.to_point(&map.buffer_snapshot);
7891 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7892 end.row -= 1;
7893 }
7894
7895 let buffer_start = map.prev_line_boundary(start).0;
7896 let buffer_end = map.next_line_boundary(end).0;
7897 buffer_start.row..buffer_end.row + 1
7898 }
7899}
7900
7901impl<T: InvalidationRegion> InvalidationStack<T> {
7902 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7903 where
7904 S: Clone + ToOffset,
7905 {
7906 while let Some(region) = self.last() {
7907 let all_selections_inside_invalidation_ranges =
7908 if selections.len() == region.ranges().len() {
7909 selections
7910 .iter()
7911 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7912 .all(|(selection, invalidation_range)| {
7913 let head = selection.head().to_offset(buffer);
7914 invalidation_range.start <= head && invalidation_range.end >= head
7915 })
7916 } else {
7917 false
7918 };
7919
7920 if all_selections_inside_invalidation_ranges {
7921 break;
7922 } else {
7923 self.pop();
7924 }
7925 }
7926 }
7927}
7928
7929impl<T> Default for InvalidationStack<T> {
7930 fn default() -> Self {
7931 Self(Default::default())
7932 }
7933}
7934
7935impl<T> Deref for InvalidationStack<T> {
7936 type Target = Vec<T>;
7937
7938 fn deref(&self) -> &Self::Target {
7939 &self.0
7940 }
7941}
7942
7943impl<T> DerefMut for InvalidationStack<T> {
7944 fn deref_mut(&mut self) -> &mut Self::Target {
7945 &mut self.0
7946 }
7947}
7948
7949impl InvalidationRegion for SnippetState {
7950 fn ranges(&self) -> &[Range<Anchor>] {
7951 &self.ranges[self.active_index]
7952 }
7953}
7954
7955impl Deref for EditorStyle {
7956 type Target = theme::Editor;
7957
7958 fn deref(&self) -> &Self::Target {
7959 &self.theme
7960 }
7961}
7962
7963pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7964 let mut highlighted_lines = Vec::new();
7965
7966 for (index, line) in diagnostic.message.lines().enumerate() {
7967 let line = match &diagnostic.source {
7968 Some(source) if index == 0 => {
7969 let source_highlight = Vec::from_iter(0..source.len());
7970 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7971 }
7972
7973 _ => highlight_diagnostic_message(Vec::new(), line),
7974 };
7975 highlighted_lines.push(line);
7976 }
7977 let message = diagnostic.message;
7978 Arc::new(move |cx: &mut BlockContext| {
7979 let message = message.clone();
7980 let settings = settings::get::<ThemeSettings>(cx);
7981 let tooltip_style = settings.theme.tooltip.clone();
7982 let theme = &settings.theme.editor;
7983 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7984 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
7985 let anchor_x = cx.anchor_x;
7986 enum BlockContextToolip {}
7987 MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
7988 Flex::column()
7989 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7990 Label::new(
7991 line.clone(),
7992 style.message.clone().with_font_size(font_size),
7993 )
7994 .with_highlights(highlights.clone())
7995 .contained()
7996 .with_margin_left(anchor_x)
7997 }))
7998 .aligned()
7999 .left()
8000 .into_any()
8001 })
8002 .with_cursor_style(CursorStyle::PointingHand)
8003 .on_click(MouseButton::Left, move |_, _, cx| {
8004 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8005 })
8006 // We really need to rethink this ID system...
8007 .with_tooltip::<BlockContextToolip>(
8008 cx.block_id,
8009 "Copy diagnostic message".to_string(),
8010 None,
8011 tooltip_style,
8012 cx,
8013 )
8014 .into_any()
8015 })
8016}
8017
8018pub fn highlight_diagnostic_message(
8019 initial_highlights: Vec<usize>,
8020 message: &str,
8021) -> (String, Vec<usize>) {
8022 let mut message_without_backticks = String::new();
8023 let mut prev_offset = 0;
8024 let mut inside_block = false;
8025 let mut highlights = initial_highlights;
8026 for (match_ix, (offset, _)) in message
8027 .match_indices('`')
8028 .chain([(message.len(), "")])
8029 .enumerate()
8030 {
8031 message_without_backticks.push_str(&message[prev_offset..offset]);
8032 if inside_block {
8033 highlights.extend(prev_offset - match_ix..offset - match_ix);
8034 }
8035
8036 inside_block = !inside_block;
8037 prev_offset = offset + 1;
8038 }
8039
8040 (message_without_backticks, highlights)
8041}
8042
8043pub fn diagnostic_style(
8044 severity: DiagnosticSeverity,
8045 valid: bool,
8046 theme: &theme::Editor,
8047) -> DiagnosticStyle {
8048 match (severity, valid) {
8049 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8050 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8051 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8052 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8053 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8054 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8055 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8056 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8057 _ => theme.invalid_hint_diagnostic.clone(),
8058 }
8059}
8060
8061pub fn combine_syntax_and_fuzzy_match_highlights(
8062 text: &str,
8063 default_style: HighlightStyle,
8064 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8065 match_indices: &[usize],
8066) -> Vec<(Range<usize>, HighlightStyle)> {
8067 let mut result = Vec::new();
8068 let mut match_indices = match_indices.iter().copied().peekable();
8069
8070 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8071 {
8072 syntax_highlight.weight = None;
8073
8074 // Add highlights for any fuzzy match characters before the next
8075 // syntax highlight range.
8076 while let Some(&match_index) = match_indices.peek() {
8077 if match_index >= range.start {
8078 break;
8079 }
8080 match_indices.next();
8081 let end_index = char_ix_after(match_index, text);
8082 let mut match_style = default_style;
8083 match_style.weight = Some(fonts::Weight::BOLD);
8084 result.push((match_index..end_index, match_style));
8085 }
8086
8087 if range.start == usize::MAX {
8088 break;
8089 }
8090
8091 // Add highlights for any fuzzy match characters within the
8092 // syntax highlight range.
8093 let mut offset = range.start;
8094 while let Some(&match_index) = match_indices.peek() {
8095 if match_index >= range.end {
8096 break;
8097 }
8098
8099 match_indices.next();
8100 if match_index > offset {
8101 result.push((offset..match_index, syntax_highlight));
8102 }
8103
8104 let mut end_index = char_ix_after(match_index, text);
8105 while let Some(&next_match_index) = match_indices.peek() {
8106 if next_match_index == end_index && next_match_index < range.end {
8107 end_index = char_ix_after(next_match_index, text);
8108 match_indices.next();
8109 } else {
8110 break;
8111 }
8112 }
8113
8114 let mut match_style = syntax_highlight;
8115 match_style.weight = Some(fonts::Weight::BOLD);
8116 result.push((match_index..end_index, match_style));
8117 offset = end_index;
8118 }
8119
8120 if offset < range.end {
8121 result.push((offset..range.end, syntax_highlight));
8122 }
8123 }
8124
8125 fn char_ix_after(ix: usize, text: &str) -> usize {
8126 ix + text[ix..].chars().next().unwrap().len_utf8()
8127 }
8128
8129 result
8130}
8131
8132pub fn styled_runs_for_code_label<'a>(
8133 label: &'a CodeLabel,
8134 syntax_theme: &'a theme::SyntaxTheme,
8135) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8136 let fade_out = HighlightStyle {
8137 fade_out: Some(0.35),
8138 ..Default::default()
8139 };
8140
8141 let mut prev_end = label.filter_range.end;
8142 label
8143 .runs
8144 .iter()
8145 .enumerate()
8146 .flat_map(move |(ix, (range, highlight_id))| {
8147 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8148 style
8149 } else {
8150 return Default::default();
8151 };
8152 let mut muted_style = style;
8153 muted_style.highlight(fade_out);
8154
8155 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8156 if range.start >= label.filter_range.end {
8157 if range.start > prev_end {
8158 runs.push((prev_end..range.start, fade_out));
8159 }
8160 runs.push((range.clone(), muted_style));
8161 } else if range.end <= label.filter_range.end {
8162 runs.push((range.clone(), style));
8163 } else {
8164 runs.push((range.start..label.filter_range.end, style));
8165 runs.push((label.filter_range.end..range.end, muted_style));
8166 }
8167 prev_end = cmp::max(prev_end, range.end);
8168
8169 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8170 runs.push((prev_end..label.text.len(), fade_out));
8171 }
8172
8173 runs
8174 })
8175}
8176
8177pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8178 let mut index = 0;
8179 let mut codepoints = text.char_indices().peekable();
8180
8181 std::iter::from_fn(move || {
8182 let start_index = index;
8183 while let Some((new_index, codepoint)) = codepoints.next() {
8184 index = new_index + codepoint.len_utf8();
8185 let current_upper = codepoint.is_uppercase();
8186 let next_upper = codepoints
8187 .peek()
8188 .map(|(_, c)| c.is_uppercase())
8189 .unwrap_or(false);
8190
8191 if !current_upper && next_upper {
8192 return Some(&text[start_index..index]);
8193 }
8194 }
8195
8196 index = text.len();
8197 if start_index < text.len() {
8198 return Some(&text[start_index..]);
8199 }
8200 None
8201 })
8202 .flat_map(|word| word.split_inclusive('_'))
8203}
8204
8205trait RangeToAnchorExt {
8206 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8207}
8208
8209impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8210 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8211 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8212 }
8213}