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