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 snapshot
2223 .chars_for_range(range)
2224 .skip_while(|c| c.is_whitespace())
2225 .take(delimiter.len())
2226 .eq(delimiter.chars())
2227 })
2228 .then(|| delimiter.clone())
2229 } else {
2230 None
2231 };
2232 (comment_delimiter, insert_extra_newline)
2233 } else {
2234 (None, false)
2235 };
2236
2237 let capacity_for_delimiter = comment_delimiter
2238 .as_deref()
2239 .map(str::len)
2240 .unwrap_or_default();
2241 let mut new_text =
2242 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2243 new_text.push_str("\n");
2244 new_text.extend(indent.chars());
2245 if let Some(delimiter) = &comment_delimiter {
2246 new_text.push_str(&delimiter);
2247 }
2248 if insert_extra_newline {
2249 new_text = new_text.repeat(2);
2250 }
2251
2252 let anchor = buffer.anchor_after(end);
2253 let new_selection = selection.map(|_| anchor);
2254 (
2255 (start..end, new_text),
2256 (insert_extra_newline, new_selection),
2257 )
2258 })
2259 .unzip()
2260 };
2261
2262 this.edit_with_autoindent(edits, cx);
2263 let buffer = this.buffer.read(cx).snapshot(cx);
2264 let new_selections = selection_fixup_info
2265 .into_iter()
2266 .map(|(extra_newline_inserted, new_selection)| {
2267 let mut cursor = new_selection.end.to_point(&buffer);
2268 if extra_newline_inserted {
2269 cursor.row -= 1;
2270 cursor.column = buffer.line_len(cursor.row);
2271 }
2272 new_selection.map(|_| cursor)
2273 })
2274 .collect();
2275
2276 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2277 this.refresh_copilot_suggestions(true, cx);
2278 });
2279 }
2280
2281 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2282 let buffer = self.buffer.read(cx);
2283 let snapshot = buffer.snapshot(cx);
2284
2285 let mut edits = Vec::new();
2286 let mut rows = Vec::new();
2287 let mut rows_inserted = 0;
2288
2289 for selection in self.selections.all_adjusted(cx) {
2290 let cursor = selection.head();
2291 let row = cursor.row;
2292
2293 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2294
2295 let newline = "\n".to_string();
2296 edits.push((start_of_line..start_of_line, newline));
2297
2298 rows.push(row + rows_inserted);
2299 rows_inserted += 1;
2300 }
2301
2302 self.transact(cx, |editor, cx| {
2303 editor.edit(edits, cx);
2304
2305 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2306 let mut index = 0;
2307 s.move_cursors_with(|map, _, _| {
2308 let row = rows[index];
2309 index += 1;
2310
2311 let point = Point::new(row, 0);
2312 let boundary = map.next_line_boundary(point).1;
2313 let clipped = map.clip_point(boundary, Bias::Left);
2314
2315 (clipped, SelectionGoal::None)
2316 });
2317 });
2318
2319 let mut indent_edits = Vec::new();
2320 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2321 for row in rows {
2322 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2323 for (row, indent) in indents {
2324 if indent.len == 0 {
2325 continue;
2326 }
2327
2328 let text = match indent.kind {
2329 IndentKind::Space => " ".repeat(indent.len as usize),
2330 IndentKind::Tab => "\t".repeat(indent.len as usize),
2331 };
2332 let point = Point::new(row, 0);
2333 indent_edits.push((point..point, text));
2334 }
2335 }
2336 editor.edit(indent_edits, cx);
2337 });
2338 }
2339
2340 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2341 let buffer = self.buffer.read(cx);
2342 let snapshot = buffer.snapshot(cx);
2343
2344 let mut edits = Vec::new();
2345 let mut rows = Vec::new();
2346 let mut rows_inserted = 0;
2347
2348 for selection in self.selections.all_adjusted(cx) {
2349 let cursor = selection.head();
2350 let row = cursor.row;
2351
2352 let point = Point::new(row + 1, 0);
2353 let start_of_line = snapshot.clip_point(point, Bias::Left);
2354
2355 let newline = "\n".to_string();
2356 edits.push((start_of_line..start_of_line, newline));
2357
2358 rows_inserted += 1;
2359 rows.push(row + rows_inserted);
2360 }
2361
2362 self.transact(cx, |editor, cx| {
2363 editor.edit(edits, cx);
2364
2365 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2366 let mut index = 0;
2367 s.move_cursors_with(|map, _, _| {
2368 let row = rows[index];
2369 index += 1;
2370
2371 let point = Point::new(row, 0);
2372 let boundary = map.next_line_boundary(point).1;
2373 let clipped = map.clip_point(boundary, Bias::Left);
2374
2375 (clipped, SelectionGoal::None)
2376 });
2377 });
2378
2379 let mut indent_edits = Vec::new();
2380 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2381 for row in rows {
2382 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2383 for (row, indent) in indents {
2384 if indent.len == 0 {
2385 continue;
2386 }
2387
2388 let text = match indent.kind {
2389 IndentKind::Space => " ".repeat(indent.len as usize),
2390 IndentKind::Tab => "\t".repeat(indent.len as usize),
2391 };
2392 let point = Point::new(row, 0);
2393 indent_edits.push((point..point, text));
2394 }
2395 }
2396 editor.edit(indent_edits, cx);
2397 });
2398 }
2399
2400 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2401 self.insert_with_autoindent_mode(
2402 text,
2403 Some(AutoindentMode::Block {
2404 original_indent_columns: Vec::new(),
2405 }),
2406 cx,
2407 );
2408 }
2409
2410 fn insert_with_autoindent_mode(
2411 &mut self,
2412 text: &str,
2413 autoindent_mode: Option<AutoindentMode>,
2414 cx: &mut ViewContext<Self>,
2415 ) {
2416 if self.read_only {
2417 return;
2418 }
2419
2420 let text: Arc<str> = text.into();
2421 self.transact(cx, |this, cx| {
2422 let old_selections = this.selections.all_adjusted(cx);
2423 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2424 let anchors = {
2425 let snapshot = buffer.read(cx);
2426 old_selections
2427 .iter()
2428 .map(|s| {
2429 let anchor = snapshot.anchor_after(s.head());
2430 s.map(|_| anchor)
2431 })
2432 .collect::<Vec<_>>()
2433 };
2434 buffer.edit(
2435 old_selections
2436 .iter()
2437 .map(|s| (s.start..s.end, text.clone())),
2438 autoindent_mode,
2439 cx,
2440 );
2441 anchors
2442 });
2443
2444 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2445 s.select_anchors(selection_anchors);
2446 })
2447 });
2448 }
2449
2450 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2451 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2452 return;
2453 }
2454
2455 let selection = self.selections.newest_anchor();
2456 if self
2457 .buffer
2458 .read(cx)
2459 .is_completion_trigger(selection.head(), text, cx)
2460 {
2461 self.show_completions(&ShowCompletions, cx);
2462 } else {
2463 self.hide_context_menu(cx);
2464 }
2465 }
2466
2467 /// If any empty selections is touching the start of its innermost containing autoclose
2468 /// region, expand it to select the brackets.
2469 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2470 let selections = self.selections.all::<usize>(cx);
2471 let buffer = self.buffer.read(cx).read(cx);
2472 let mut new_selections = Vec::new();
2473 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2474 if let (Some(region), true) = (region, selection.is_empty()) {
2475 let mut range = region.range.to_offset(&buffer);
2476 if selection.start == range.start {
2477 if range.start >= region.pair.start.len() {
2478 range.start -= region.pair.start.len();
2479 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2480 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2481 range.end += region.pair.end.len();
2482 selection.start = range.start;
2483 selection.end = range.end;
2484 }
2485 }
2486 }
2487 }
2488 }
2489 new_selections.push(selection);
2490 }
2491
2492 drop(buffer);
2493 self.change_selections(None, cx, |selections| selections.select(new_selections));
2494 }
2495
2496 /// Iterate the given selections, and for each one, find the smallest surrounding
2497 /// autoclose region. This uses the ordering of the selections and the autoclose
2498 /// regions to avoid repeated comparisons.
2499 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2500 &'a self,
2501 selections: impl IntoIterator<Item = Selection<D>>,
2502 buffer: &'a MultiBufferSnapshot,
2503 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2504 let mut i = 0;
2505 let mut regions = self.autoclose_regions.as_slice();
2506 selections.into_iter().map(move |selection| {
2507 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2508
2509 let mut enclosing = None;
2510 while let Some(pair_state) = regions.get(i) {
2511 if pair_state.range.end.to_offset(buffer) < range.start {
2512 regions = ®ions[i + 1..];
2513 i = 0;
2514 } else if pair_state.range.start.to_offset(buffer) > range.end {
2515 break;
2516 } else if pair_state.selection_id == selection.id {
2517 enclosing = Some(pair_state);
2518 i += 1;
2519 }
2520 }
2521
2522 (selection.clone(), enclosing)
2523 })
2524 }
2525
2526 /// Remove any autoclose regions that no longer contain their selection.
2527 fn invalidate_autoclose_regions(
2528 &mut self,
2529 mut selections: &[Selection<Anchor>],
2530 buffer: &MultiBufferSnapshot,
2531 ) {
2532 self.autoclose_regions.retain(|state| {
2533 let mut i = 0;
2534 while let Some(selection) = selections.get(i) {
2535 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2536 selections = &selections[1..];
2537 continue;
2538 }
2539 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2540 break;
2541 }
2542 if selection.id == state.selection_id {
2543 return true;
2544 } else {
2545 i += 1;
2546 }
2547 }
2548 false
2549 });
2550 }
2551
2552 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2553 let offset = position.to_offset(buffer);
2554 let (word_range, kind) = buffer.surrounding_word(offset);
2555 if offset > word_range.start && kind == Some(CharKind::Word) {
2556 Some(
2557 buffer
2558 .text_for_range(word_range.start..offset)
2559 .collect::<String>(),
2560 )
2561 } else {
2562 None
2563 }
2564 }
2565
2566 fn trigger_on_type_formatting(
2567 &self,
2568 input: String,
2569 cx: &mut ViewContext<Self>,
2570 ) -> Option<Task<Result<()>>> {
2571 if input.len() != 1 {
2572 return None;
2573 }
2574
2575 let project = self.project.as_ref()?;
2576 let position = self.selections.newest_anchor().head();
2577 let (buffer, buffer_position) = self
2578 .buffer
2579 .read(cx)
2580 .text_anchor_for_position(position.clone(), cx)?;
2581
2582 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2583 // hence we do LSP request & edit on host side only — add formats to host's history.
2584 let push_to_lsp_host_history = true;
2585 // If this is not the host, append its history with new edits.
2586 let push_to_client_history = project.read(cx).is_remote();
2587
2588 let on_type_formatting = project.update(cx, |project, cx| {
2589 project.on_type_format(
2590 buffer.clone(),
2591 buffer_position,
2592 input,
2593 push_to_lsp_host_history,
2594 cx,
2595 )
2596 });
2597 Some(cx.spawn(|editor, mut cx| async move {
2598 if let Some(transaction) = on_type_formatting.await? {
2599 if push_to_client_history {
2600 buffer.update(&mut cx, |buffer, _| {
2601 buffer.push_transaction(transaction, Instant::now());
2602 });
2603 }
2604 editor.update(&mut cx, |editor, cx| {
2605 editor.refresh_document_highlights(cx);
2606 })?;
2607 }
2608 Ok(())
2609 }))
2610 }
2611
2612 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2613 if self.pending_rename.is_some() {
2614 return;
2615 }
2616
2617 let project = if let Some(project) = self.project.clone() {
2618 project
2619 } else {
2620 return;
2621 };
2622
2623 let position = self.selections.newest_anchor().head();
2624 let (buffer, buffer_position) = if let Some(output) = self
2625 .buffer
2626 .read(cx)
2627 .text_anchor_for_position(position.clone(), cx)
2628 {
2629 output
2630 } else {
2631 return;
2632 };
2633
2634 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2635 let completions = project.update(cx, |project, cx| {
2636 project.completions(&buffer, buffer_position, cx)
2637 });
2638
2639 let id = post_inc(&mut self.next_completion_id);
2640 let task = cx.spawn(|this, mut cx| {
2641 async move {
2642 let menu = if let Some(completions) = completions.await.log_err() {
2643 let mut menu = CompletionsMenu {
2644 id,
2645 initial_position: position,
2646 match_candidates: completions
2647 .iter()
2648 .enumerate()
2649 .map(|(id, completion)| {
2650 StringMatchCandidate::new(
2651 id,
2652 completion.label.text[completion.label.filter_range.clone()]
2653 .into(),
2654 )
2655 })
2656 .collect(),
2657 buffer,
2658 completions: completions.into(),
2659 matches: Vec::new().into(),
2660 selected_item: 0,
2661 list: Default::default(),
2662 };
2663 menu.filter(query.as_deref(), cx.background()).await;
2664 if menu.matches.is_empty() {
2665 None
2666 } else {
2667 Some(menu)
2668 }
2669 } else {
2670 None
2671 };
2672
2673 this.update(&mut cx, |this, cx| {
2674 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2675
2676 match this.context_menu.as_ref() {
2677 None => {}
2678 Some(ContextMenu::Completions(prev_menu)) => {
2679 if prev_menu.id > id {
2680 return;
2681 }
2682 }
2683 _ => return,
2684 }
2685
2686 if this.focused && menu.is_some() {
2687 let menu = menu.unwrap();
2688 this.show_context_menu(ContextMenu::Completions(menu), cx);
2689 } else if this.completion_tasks.is_empty() {
2690 // If there are no more completion tasks and the last menu was
2691 // empty, we should hide it. If it was already hidden, we should
2692 // also show the copilot suggestion when available.
2693 if this.hide_context_menu(cx).is_none() {
2694 this.update_visible_copilot_suggestion(cx);
2695 }
2696 }
2697 })?;
2698
2699 Ok::<_, anyhow::Error>(())
2700 }
2701 .log_err()
2702 });
2703 self.completion_tasks.push((id, task));
2704 }
2705
2706 pub fn confirm_completion(
2707 &mut self,
2708 action: &ConfirmCompletion,
2709 cx: &mut ViewContext<Self>,
2710 ) -> Option<Task<Result<()>>> {
2711 use language::ToOffset as _;
2712
2713 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2714 menu
2715 } else {
2716 return None;
2717 };
2718
2719 let mat = completions_menu
2720 .matches
2721 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2722 let buffer_handle = completions_menu.buffer;
2723 let completion = completions_menu.completions.get(mat.candidate_id)?;
2724
2725 let snippet;
2726 let text;
2727 if completion.is_snippet() {
2728 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2729 text = snippet.as_ref().unwrap().text.clone();
2730 } else {
2731 snippet = None;
2732 text = completion.new_text.clone();
2733 };
2734 let selections = self.selections.all::<usize>(cx);
2735 let buffer = buffer_handle.read(cx);
2736 let old_range = completion.old_range.to_offset(buffer);
2737 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2738
2739 let newest_selection = self.selections.newest_anchor();
2740 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2741 return None;
2742 }
2743
2744 let lookbehind = newest_selection
2745 .start
2746 .text_anchor
2747 .to_offset(buffer)
2748 .saturating_sub(old_range.start);
2749 let lookahead = old_range
2750 .end
2751 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2752 let mut common_prefix_len = old_text
2753 .bytes()
2754 .zip(text.bytes())
2755 .take_while(|(a, b)| a == b)
2756 .count();
2757
2758 let snapshot = self.buffer.read(cx).snapshot(cx);
2759 let mut ranges = Vec::new();
2760 for selection in &selections {
2761 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2762 let start = selection.start.saturating_sub(lookbehind);
2763 let end = selection.end + lookahead;
2764 ranges.push(start + common_prefix_len..end);
2765 } else {
2766 common_prefix_len = 0;
2767 ranges.clear();
2768 ranges.extend(selections.iter().map(|s| {
2769 if s.id == newest_selection.id {
2770 old_range.clone()
2771 } else {
2772 s.start..s.end
2773 }
2774 }));
2775 break;
2776 }
2777 }
2778 let text = &text[common_prefix_len..];
2779
2780 self.transact(cx, |this, cx| {
2781 if let Some(mut snippet) = snippet {
2782 snippet.text = text.to_string();
2783 for tabstop in snippet.tabstops.iter_mut().flatten() {
2784 tabstop.start -= common_prefix_len as isize;
2785 tabstop.end -= common_prefix_len as isize;
2786 }
2787
2788 this.insert_snippet(&ranges, snippet, cx).log_err();
2789 } else {
2790 this.buffer.update(cx, |buffer, cx| {
2791 buffer.edit(
2792 ranges.iter().map(|range| (range.clone(), text)),
2793 Some(AutoindentMode::EachLine),
2794 cx,
2795 );
2796 });
2797 }
2798
2799 this.refresh_copilot_suggestions(true, cx);
2800 });
2801
2802 let project = self.project.clone()?;
2803 let apply_edits = project.update(cx, |project, cx| {
2804 project.apply_additional_edits_for_completion(
2805 buffer_handle,
2806 completion.clone(),
2807 true,
2808 cx,
2809 )
2810 });
2811 Some(cx.foreground().spawn(async move {
2812 apply_edits.await?;
2813 Ok(())
2814 }))
2815 }
2816
2817 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2818 if matches!(
2819 self.context_menu.as_ref(),
2820 Some(ContextMenu::CodeActions(_))
2821 ) {
2822 self.context_menu.take();
2823 cx.notify();
2824 return;
2825 }
2826
2827 let deployed_from_indicator = action.deployed_from_indicator;
2828 let mut task = self.code_actions_task.take();
2829 cx.spawn(|this, mut cx| async move {
2830 while let Some(prev_task) = task {
2831 prev_task.await;
2832 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
2833 }
2834
2835 this.update(&mut cx, |this, cx| {
2836 if this.focused {
2837 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2838 this.show_context_menu(
2839 ContextMenu::CodeActions(CodeActionsMenu {
2840 buffer,
2841 actions,
2842 selected_item: Default::default(),
2843 list: Default::default(),
2844 deployed_from_indicator,
2845 }),
2846 cx,
2847 );
2848 }
2849 }
2850 })?;
2851
2852 Ok::<_, anyhow::Error>(())
2853 })
2854 .detach_and_log_err(cx);
2855 }
2856
2857 pub fn confirm_code_action(
2858 workspace: &mut Workspace,
2859 action: &ConfirmCodeAction,
2860 cx: &mut ViewContext<Workspace>,
2861 ) -> Option<Task<Result<()>>> {
2862 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2863 let actions_menu = if let ContextMenu::CodeActions(menu) =
2864 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2865 {
2866 menu
2867 } else {
2868 return None;
2869 };
2870 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2871 let action = actions_menu.actions.get(action_ix)?.clone();
2872 let title = action.lsp_action.title.clone();
2873 let buffer = actions_menu.buffer;
2874
2875 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2876 project.apply_code_action(buffer, action, true, cx)
2877 });
2878 let editor = editor.downgrade();
2879 Some(cx.spawn(|workspace, cx| async move {
2880 let project_transaction = apply_code_actions.await?;
2881 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
2882 }))
2883 }
2884
2885 async fn open_project_transaction(
2886 this: &WeakViewHandle<Editor>,
2887 workspace: WeakViewHandle<Workspace>,
2888 transaction: ProjectTransaction,
2889 title: String,
2890 mut cx: AsyncAppContext,
2891 ) -> Result<()> {
2892 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
2893
2894 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2895 entries.sort_unstable_by_key(|(buffer, _)| {
2896 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2897 });
2898
2899 // If the project transaction's edits are all contained within this editor, then
2900 // avoid opening a new editor to display them.
2901
2902 if let Some((buffer, transaction)) = entries.first() {
2903 if entries.len() == 1 {
2904 let excerpt = this.read_with(&cx, |editor, cx| {
2905 editor
2906 .buffer()
2907 .read(cx)
2908 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2909 })?;
2910 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
2911 if excerpted_buffer == *buffer {
2912 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
2913 let excerpt_range = excerpt_range.to_offset(buffer);
2914 buffer
2915 .edited_ranges_for_transaction::<usize>(transaction)
2916 .all(|range| {
2917 excerpt_range.start <= range.start
2918 && excerpt_range.end >= range.end
2919 })
2920 });
2921
2922 if all_edits_within_excerpt {
2923 return Ok(());
2924 }
2925 }
2926 }
2927 }
2928 } else {
2929 return Ok(());
2930 }
2931
2932 let mut ranges_to_highlight = Vec::new();
2933 let excerpt_buffer = cx.add_model(|cx| {
2934 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2935 for (buffer_handle, transaction) in &entries {
2936 let buffer = buffer_handle.read(cx);
2937 ranges_to_highlight.extend(
2938 multibuffer.push_excerpts_with_context_lines(
2939 buffer_handle.clone(),
2940 buffer
2941 .edited_ranges_for_transaction::<usize>(transaction)
2942 .collect(),
2943 1,
2944 cx,
2945 ),
2946 );
2947 }
2948 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
2949 multibuffer
2950 });
2951
2952 workspace.update(&mut cx, |workspace, cx| {
2953 let project = workspace.project().clone();
2954 let editor =
2955 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2956 workspace.add_item(Box::new(editor.clone()), cx);
2957 editor.update(cx, |editor, cx| {
2958 editor.highlight_background::<Self>(
2959 ranges_to_highlight,
2960 |theme| theme.editor.highlighted_line_background,
2961 cx,
2962 );
2963 });
2964 })?;
2965
2966 Ok(())
2967 }
2968
2969 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2970 let project = self.project.as_ref()?;
2971 let buffer = self.buffer.read(cx);
2972 let newest_selection = self.selections.newest_anchor().clone();
2973 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2974 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2975 if start_buffer != end_buffer {
2976 return None;
2977 }
2978
2979 let actions = project.update(cx, |project, cx| {
2980 project.code_actions(&start_buffer, start..end, cx)
2981 });
2982 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
2983 let actions = actions.await;
2984 this.update(&mut cx, |this, cx| {
2985 this.available_code_actions = actions.log_err().and_then(|actions| {
2986 if actions.is_empty() {
2987 None
2988 } else {
2989 Some((start_buffer, actions.into()))
2990 }
2991 });
2992 cx.notify();
2993 })
2994 .log_err();
2995 }));
2996 None
2997 }
2998
2999 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3000 if self.pending_rename.is_some() {
3001 return None;
3002 }
3003
3004 let project = self.project.as_ref()?;
3005 let buffer = self.buffer.read(cx);
3006 let newest_selection = self.selections.newest_anchor().clone();
3007 let cursor_position = newest_selection.head();
3008 let (cursor_buffer, cursor_buffer_position) =
3009 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3010 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3011 if cursor_buffer != tail_buffer {
3012 return None;
3013 }
3014
3015 let highlights = project.update(cx, |project, cx| {
3016 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3017 });
3018
3019 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3020 if let Some(highlights) = highlights.await.log_err() {
3021 this.update(&mut cx, |this, cx| {
3022 if this.pending_rename.is_some() {
3023 return;
3024 }
3025
3026 let buffer_id = cursor_position.buffer_id;
3027 let buffer = this.buffer.read(cx);
3028 if !buffer
3029 .text_anchor_for_position(cursor_position, cx)
3030 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3031 {
3032 return;
3033 }
3034
3035 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3036 let mut write_ranges = Vec::new();
3037 let mut read_ranges = Vec::new();
3038 for highlight in highlights {
3039 for (excerpt_id, excerpt_range) in
3040 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3041 {
3042 let start = highlight
3043 .range
3044 .start
3045 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3046 let end = highlight
3047 .range
3048 .end
3049 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3050 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3051 continue;
3052 }
3053
3054 let range = Anchor {
3055 buffer_id,
3056 excerpt_id: excerpt_id.clone(),
3057 text_anchor: start,
3058 }..Anchor {
3059 buffer_id,
3060 excerpt_id,
3061 text_anchor: end,
3062 };
3063 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3064 write_ranges.push(range);
3065 } else {
3066 read_ranges.push(range);
3067 }
3068 }
3069 }
3070
3071 this.highlight_background::<DocumentHighlightRead>(
3072 read_ranges,
3073 |theme| theme.editor.document_highlight_read_background,
3074 cx,
3075 );
3076 this.highlight_background::<DocumentHighlightWrite>(
3077 write_ranges,
3078 |theme| theme.editor.document_highlight_write_background,
3079 cx,
3080 );
3081 cx.notify();
3082 })
3083 .log_err();
3084 }
3085 }));
3086 None
3087 }
3088
3089 fn refresh_copilot_suggestions(
3090 &mut self,
3091 debounce: bool,
3092 cx: &mut ViewContext<Self>,
3093 ) -> Option<()> {
3094 let copilot = Copilot::global(cx)?;
3095 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3096 self.clear_copilot_suggestions(cx);
3097 return None;
3098 }
3099 self.update_visible_copilot_suggestion(cx);
3100
3101 let snapshot = self.buffer.read(cx).snapshot(cx);
3102 let cursor = self.selections.newest_anchor().head();
3103 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3104 self.clear_copilot_suggestions(cx);
3105 return None;
3106 }
3107
3108 let (buffer, buffer_position) =
3109 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3110 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3111 if debounce {
3112 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3113 }
3114
3115 let completions = copilot
3116 .update(&mut cx, |copilot, cx| {
3117 copilot.completions(&buffer, buffer_position, cx)
3118 })
3119 .await
3120 .log_err()
3121 .into_iter()
3122 .flatten()
3123 .collect_vec();
3124
3125 this.update(&mut cx, |this, cx| {
3126 if !completions.is_empty() {
3127 this.copilot_state.cycled = false;
3128 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3129 this.copilot_state.completions.clear();
3130 this.copilot_state.active_completion_index = 0;
3131 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3132 for completion in completions {
3133 this.copilot_state.push_completion(completion);
3134 }
3135 this.update_visible_copilot_suggestion(cx);
3136 }
3137 })
3138 .log_err()?;
3139 Some(())
3140 });
3141
3142 Some(())
3143 }
3144
3145 fn cycle_copilot_suggestions(
3146 &mut self,
3147 direction: Direction,
3148 cx: &mut ViewContext<Self>,
3149 ) -> Option<()> {
3150 let copilot = Copilot::global(cx)?;
3151 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3152 return None;
3153 }
3154
3155 if self.copilot_state.cycled {
3156 self.copilot_state.cycle_completions(direction);
3157 self.update_visible_copilot_suggestion(cx);
3158 } else {
3159 let cursor = self.selections.newest_anchor().head();
3160 let (buffer, buffer_position) =
3161 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3162 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3163 let completions = copilot
3164 .update(&mut cx, |copilot, cx| {
3165 copilot.completions_cycling(&buffer, buffer_position, cx)
3166 })
3167 .await;
3168
3169 this.update(&mut cx, |this, cx| {
3170 this.copilot_state.cycled = true;
3171 for completion in completions.log_err().into_iter().flatten() {
3172 this.copilot_state.push_completion(completion);
3173 }
3174 this.copilot_state.cycle_completions(direction);
3175 this.update_visible_copilot_suggestion(cx);
3176 })
3177 .log_err()?;
3178
3179 Some(())
3180 });
3181 }
3182
3183 Some(())
3184 }
3185
3186 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3187 if !self.has_active_copilot_suggestion(cx) {
3188 self.refresh_copilot_suggestions(false, cx);
3189 return;
3190 }
3191
3192 self.update_visible_copilot_suggestion(cx);
3193 }
3194
3195 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3196 if self.has_active_copilot_suggestion(cx) {
3197 self.cycle_copilot_suggestions(Direction::Next, cx);
3198 } else {
3199 self.refresh_copilot_suggestions(false, cx);
3200 }
3201 }
3202
3203 fn previous_copilot_suggestion(
3204 &mut self,
3205 _: &copilot::PreviousSuggestion,
3206 cx: &mut ViewContext<Self>,
3207 ) {
3208 if self.has_active_copilot_suggestion(cx) {
3209 self.cycle_copilot_suggestions(Direction::Prev, cx);
3210 } else {
3211 self.refresh_copilot_suggestions(false, cx);
3212 }
3213 }
3214
3215 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3216 if let Some(suggestion) = self
3217 .display_map
3218 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx))
3219 {
3220 if let Some((copilot, completion)) =
3221 Copilot::global(cx).zip(self.copilot_state.active_completion())
3222 {
3223 copilot
3224 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3225 .detach_and_log_err(cx);
3226
3227 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3228 }
3229 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3230 cx.notify();
3231 true
3232 } else {
3233 false
3234 }
3235 }
3236
3237 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3238 if self.has_active_copilot_suggestion(cx) {
3239 if let Some(copilot) = Copilot::global(cx) {
3240 copilot
3241 .update(cx, |copilot, cx| {
3242 copilot.discard_completions(&self.copilot_state.completions, cx)
3243 })
3244 .detach_and_log_err(cx);
3245
3246 self.report_copilot_event(None, false, cx)
3247 }
3248
3249 self.display_map
3250 .update(cx, |map, cx| map.replace_suggestion::<usize>(None, cx));
3251 cx.notify();
3252 true
3253 } else {
3254 false
3255 }
3256 }
3257
3258 fn is_copilot_enabled_at(
3259 &self,
3260 location: Anchor,
3261 snapshot: &MultiBufferSnapshot,
3262 cx: &mut ViewContext<Self>,
3263 ) -> bool {
3264 let file = snapshot.file_at(location);
3265 let language = snapshot.language_at(location);
3266 let settings = all_language_settings(file, cx);
3267 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3268 }
3269
3270 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3271 self.display_map.read(cx).has_suggestion()
3272 }
3273
3274 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3275 let snapshot = self.buffer.read(cx).snapshot(cx);
3276 let selection = self.selections.newest_anchor();
3277 let cursor = selection.head();
3278
3279 if self.context_menu.is_some()
3280 || !self.completion_tasks.is_empty()
3281 || selection.start != selection.end
3282 {
3283 self.discard_copilot_suggestion(cx);
3284 } else if let Some(text) = self
3285 .copilot_state
3286 .text_for_active_completion(cursor, &snapshot)
3287 {
3288 self.display_map.update(cx, move |map, cx| {
3289 map.replace_suggestion(
3290 Some(Suggestion {
3291 position: cursor,
3292 text: text.trim_end().into(),
3293 }),
3294 cx,
3295 )
3296 });
3297 cx.notify();
3298 } else {
3299 self.discard_copilot_suggestion(cx);
3300 }
3301 }
3302
3303 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3304 self.copilot_state = Default::default();
3305 self.discard_copilot_suggestion(cx);
3306 }
3307
3308 pub fn render_code_actions_indicator(
3309 &self,
3310 style: &EditorStyle,
3311 active: bool,
3312 cx: &mut ViewContext<Self>,
3313 ) -> Option<AnyElement<Self>> {
3314 if self.available_code_actions.is_some() {
3315 enum CodeActions {}
3316 Some(
3317 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3318 Svg::new("icons/bolt_8.svg")
3319 .with_color(style.code_actions.indicator.style_for(state, active).color)
3320 })
3321 .with_cursor_style(CursorStyle::PointingHand)
3322 .with_padding(Padding::uniform(3.))
3323 .on_down(MouseButton::Left, |_, this, cx| {
3324 this.toggle_code_actions(
3325 &ToggleCodeActions {
3326 deployed_from_indicator: true,
3327 },
3328 cx,
3329 );
3330 })
3331 .into_any(),
3332 )
3333 } else {
3334 None
3335 }
3336 }
3337
3338 pub fn render_fold_indicators(
3339 &self,
3340 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3341 style: &EditorStyle,
3342 gutter_hovered: bool,
3343 line_height: f32,
3344 gutter_margin: f32,
3345 cx: &mut ViewContext<Self>,
3346 ) -> Vec<Option<AnyElement<Self>>> {
3347 enum FoldIndicators {}
3348
3349 let style = style.folds.clone();
3350
3351 fold_data
3352 .iter()
3353 .enumerate()
3354 .map(|(ix, fold_data)| {
3355 fold_data
3356 .map(|(fold_status, buffer_row, active)| {
3357 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3358 MouseEventHandler::<FoldIndicators, _>::new(
3359 ix as usize,
3360 cx,
3361 |mouse_state, _| {
3362 Svg::new(match fold_status {
3363 FoldStatus::Folded => style.folded_icon.clone(),
3364 FoldStatus::Foldable => style.foldable_icon.clone(),
3365 })
3366 .with_color(
3367 style
3368 .indicator
3369 .style_for(
3370 mouse_state,
3371 fold_status == FoldStatus::Folded,
3372 )
3373 .color,
3374 )
3375 .constrained()
3376 .with_width(gutter_margin * style.icon_margin_scale)
3377 .aligned()
3378 .constrained()
3379 .with_height(line_height)
3380 .with_width(gutter_margin)
3381 .aligned()
3382 },
3383 )
3384 .with_cursor_style(CursorStyle::PointingHand)
3385 .with_padding(Padding::uniform(3.))
3386 .on_click(MouseButton::Left, {
3387 move |_, editor, cx| match fold_status {
3388 FoldStatus::Folded => {
3389 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3390 }
3391 FoldStatus::Foldable => {
3392 editor.fold_at(&FoldAt { buffer_row }, cx);
3393 }
3394 }
3395 })
3396 .into_any()
3397 })
3398 })
3399 .flatten()
3400 })
3401 .collect()
3402 }
3403
3404 pub fn context_menu_visible(&self) -> bool {
3405 self.context_menu
3406 .as_ref()
3407 .map_or(false, |menu| menu.visible())
3408 }
3409
3410 pub fn render_context_menu(
3411 &self,
3412 cursor_position: DisplayPoint,
3413 style: EditorStyle,
3414 cx: &mut ViewContext<Editor>,
3415 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3416 self.context_menu
3417 .as_ref()
3418 .map(|menu| menu.render(cursor_position, style, cx))
3419 }
3420
3421 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3422 if !matches!(menu, ContextMenu::Completions(_)) {
3423 self.completion_tasks.clear();
3424 }
3425 self.context_menu = Some(menu);
3426 self.discard_copilot_suggestion(cx);
3427 cx.notify();
3428 }
3429
3430 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3431 cx.notify();
3432 self.completion_tasks.clear();
3433 let context_menu = self.context_menu.take();
3434 if context_menu.is_some() {
3435 self.update_visible_copilot_suggestion(cx);
3436 }
3437 context_menu
3438 }
3439
3440 pub fn insert_snippet(
3441 &mut self,
3442 insertion_ranges: &[Range<usize>],
3443 snippet: Snippet,
3444 cx: &mut ViewContext<Self>,
3445 ) -> Result<()> {
3446 let tabstops = self.buffer.update(cx, |buffer, cx| {
3447 let snippet_text: Arc<str> = snippet.text.clone().into();
3448 buffer.edit(
3449 insertion_ranges
3450 .iter()
3451 .cloned()
3452 .map(|range| (range, snippet_text.clone())),
3453 Some(AutoindentMode::EachLine),
3454 cx,
3455 );
3456
3457 let snapshot = &*buffer.read(cx);
3458 let snippet = &snippet;
3459 snippet
3460 .tabstops
3461 .iter()
3462 .map(|tabstop| {
3463 let mut tabstop_ranges = tabstop
3464 .iter()
3465 .flat_map(|tabstop_range| {
3466 let mut delta = 0_isize;
3467 insertion_ranges.iter().map(move |insertion_range| {
3468 let insertion_start = insertion_range.start as isize + delta;
3469 delta +=
3470 snippet.text.len() as isize - insertion_range.len() as isize;
3471
3472 let start = snapshot.anchor_before(
3473 (insertion_start + tabstop_range.start) as usize,
3474 );
3475 let end = snapshot
3476 .anchor_after((insertion_start + tabstop_range.end) as usize);
3477 start..end
3478 })
3479 })
3480 .collect::<Vec<_>>();
3481 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3482 tabstop_ranges
3483 })
3484 .collect::<Vec<_>>()
3485 });
3486
3487 if let Some(tabstop) = tabstops.first() {
3488 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3489 s.select_ranges(tabstop.iter().cloned());
3490 });
3491 self.snippet_stack.push(SnippetState {
3492 active_index: 0,
3493 ranges: tabstops,
3494 });
3495 }
3496
3497 Ok(())
3498 }
3499
3500 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3501 self.move_to_snippet_tabstop(Bias::Right, cx)
3502 }
3503
3504 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3505 self.move_to_snippet_tabstop(Bias::Left, cx)
3506 }
3507
3508 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3509 if let Some(mut snippet) = self.snippet_stack.pop() {
3510 match bias {
3511 Bias::Left => {
3512 if snippet.active_index > 0 {
3513 snippet.active_index -= 1;
3514 } else {
3515 self.snippet_stack.push(snippet);
3516 return false;
3517 }
3518 }
3519 Bias::Right => {
3520 if snippet.active_index + 1 < snippet.ranges.len() {
3521 snippet.active_index += 1;
3522 } else {
3523 self.snippet_stack.push(snippet);
3524 return false;
3525 }
3526 }
3527 }
3528 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3529 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3530 s.select_anchor_ranges(current_ranges.iter().cloned())
3531 });
3532 // If snippet state is not at the last tabstop, push it back on the stack
3533 if snippet.active_index + 1 < snippet.ranges.len() {
3534 self.snippet_stack.push(snippet);
3535 }
3536 return true;
3537 }
3538 }
3539
3540 false
3541 }
3542
3543 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3544 self.transact(cx, |this, cx| {
3545 this.select_all(&SelectAll, cx);
3546 this.insert("", cx);
3547 });
3548 }
3549
3550 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3551 self.transact(cx, |this, cx| {
3552 this.select_autoclose_pair(cx);
3553 let mut selections = this.selections.all::<Point>(cx);
3554 if !this.selections.line_mode {
3555 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3556 for selection in &mut selections {
3557 if selection.is_empty() {
3558 let old_head = selection.head();
3559 let mut new_head =
3560 movement::left(&display_map, old_head.to_display_point(&display_map))
3561 .to_point(&display_map);
3562 if let Some((buffer, line_buffer_range)) = display_map
3563 .buffer_snapshot
3564 .buffer_line_for_row(old_head.row)
3565 {
3566 let indent_size =
3567 buffer.indent_size_for_line(line_buffer_range.start.row);
3568 let indent_len = match indent_size.kind {
3569 IndentKind::Space => {
3570 buffer.settings_at(line_buffer_range.start, cx).tab_size
3571 }
3572 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3573 };
3574 if old_head.column <= indent_size.len && old_head.column > 0 {
3575 let indent_len = indent_len.get();
3576 new_head = cmp::min(
3577 new_head,
3578 Point::new(
3579 old_head.row,
3580 ((old_head.column - 1) / indent_len) * indent_len,
3581 ),
3582 );
3583 }
3584 }
3585
3586 selection.set_head(new_head, SelectionGoal::None);
3587 }
3588 }
3589 }
3590
3591 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3592 this.insert("", cx);
3593 this.refresh_copilot_suggestions(true, cx);
3594 });
3595 }
3596
3597 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3598 self.transact(cx, |this, cx| {
3599 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3600 let line_mode = s.line_mode;
3601 s.move_with(|map, selection| {
3602 if selection.is_empty() && !line_mode {
3603 let cursor = movement::right(map, selection.head());
3604 selection.end = cursor;
3605 selection.reversed = true;
3606 selection.goal = SelectionGoal::None;
3607 }
3608 })
3609 });
3610 this.insert("", cx);
3611 this.refresh_copilot_suggestions(true, cx);
3612 });
3613 }
3614
3615 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3616 if self.move_to_prev_snippet_tabstop(cx) {
3617 return;
3618 }
3619
3620 self.outdent(&Outdent, cx);
3621 }
3622
3623 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3624 if self.move_to_next_snippet_tabstop(cx) {
3625 return;
3626 }
3627
3628 let mut selections = self.selections.all_adjusted(cx);
3629 let buffer = self.buffer.read(cx);
3630 let snapshot = buffer.snapshot(cx);
3631 let rows_iter = selections.iter().map(|s| s.head().row);
3632 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3633
3634 let mut edits = Vec::new();
3635 let mut prev_edited_row = 0;
3636 let mut row_delta = 0;
3637 for selection in &mut selections {
3638 if selection.start.row != prev_edited_row {
3639 row_delta = 0;
3640 }
3641 prev_edited_row = selection.end.row;
3642
3643 // If the selection is non-empty, then increase the indentation of the selected lines.
3644 if !selection.is_empty() {
3645 row_delta =
3646 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3647 continue;
3648 }
3649
3650 // If the selection is empty and the cursor is in the leading whitespace before the
3651 // suggested indentation, then auto-indent the line.
3652 let cursor = selection.head();
3653 let current_indent = snapshot.indent_size_for_line(cursor.row);
3654 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3655 if cursor.column < suggested_indent.len
3656 && cursor.column <= current_indent.len
3657 && current_indent.len <= suggested_indent.len
3658 {
3659 selection.start = Point::new(cursor.row, suggested_indent.len);
3660 selection.end = selection.start;
3661 if row_delta == 0 {
3662 edits.extend(Buffer::edit_for_indent_size_adjustment(
3663 cursor.row,
3664 current_indent,
3665 suggested_indent,
3666 ));
3667 row_delta = suggested_indent.len - current_indent.len;
3668 }
3669 continue;
3670 }
3671 }
3672
3673 // Accept copilot suggestion if there is only one selection and the cursor is not
3674 // in the leading whitespace.
3675 if self.selections.count() == 1
3676 && cursor.column >= current_indent.len
3677 && self.has_active_copilot_suggestion(cx)
3678 {
3679 self.accept_copilot_suggestion(cx);
3680 return;
3681 }
3682
3683 // Otherwise, insert a hard or soft tab.
3684 let settings = buffer.settings_at(cursor, cx);
3685 let tab_size = if settings.hard_tabs {
3686 IndentSize::tab()
3687 } else {
3688 let tab_size = settings.tab_size.get();
3689 let char_column = snapshot
3690 .text_for_range(Point::new(cursor.row, 0)..cursor)
3691 .flat_map(str::chars)
3692 .count()
3693 + row_delta as usize;
3694 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3695 IndentSize::spaces(chars_to_next_tab_stop)
3696 };
3697 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3698 selection.end = selection.start;
3699 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3700 row_delta += tab_size.len;
3701 }
3702
3703 self.transact(cx, |this, cx| {
3704 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3705 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3706 this.refresh_copilot_suggestions(true, cx);
3707 });
3708 }
3709
3710 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3711 let mut selections = self.selections.all::<Point>(cx);
3712 let mut prev_edited_row = 0;
3713 let mut row_delta = 0;
3714 let mut edits = Vec::new();
3715 let buffer = self.buffer.read(cx);
3716 let snapshot = buffer.snapshot(cx);
3717 for selection in &mut selections {
3718 if selection.start.row != prev_edited_row {
3719 row_delta = 0;
3720 }
3721 prev_edited_row = selection.end.row;
3722
3723 row_delta =
3724 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3725 }
3726
3727 self.transact(cx, |this, cx| {
3728 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3729 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3730 });
3731 }
3732
3733 fn indent_selection(
3734 buffer: &MultiBuffer,
3735 snapshot: &MultiBufferSnapshot,
3736 selection: &mut Selection<Point>,
3737 edits: &mut Vec<(Range<Point>, String)>,
3738 delta_for_start_row: u32,
3739 cx: &AppContext,
3740 ) -> u32 {
3741 let settings = buffer.settings_at(selection.start, cx);
3742 let tab_size = settings.tab_size.get();
3743 let indent_kind = if settings.hard_tabs {
3744 IndentKind::Tab
3745 } else {
3746 IndentKind::Space
3747 };
3748 let mut start_row = selection.start.row;
3749 let mut end_row = selection.end.row + 1;
3750
3751 // If a selection ends at the beginning of a line, don't indent
3752 // that last line.
3753 if selection.end.column == 0 {
3754 end_row -= 1;
3755 }
3756
3757 // Avoid re-indenting a row that has already been indented by a
3758 // previous selection, but still update this selection's column
3759 // to reflect that indentation.
3760 if delta_for_start_row > 0 {
3761 start_row += 1;
3762 selection.start.column += delta_for_start_row;
3763 if selection.end.row == selection.start.row {
3764 selection.end.column += delta_for_start_row;
3765 }
3766 }
3767
3768 let mut delta_for_end_row = 0;
3769 for row in start_row..end_row {
3770 let current_indent = snapshot.indent_size_for_line(row);
3771 let indent_delta = match (current_indent.kind, indent_kind) {
3772 (IndentKind::Space, IndentKind::Space) => {
3773 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
3774 IndentSize::spaces(columns_to_next_tab_stop)
3775 }
3776 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
3777 (_, IndentKind::Tab) => IndentSize::tab(),
3778 };
3779
3780 let row_start = Point::new(row, 0);
3781 edits.push((
3782 row_start..row_start,
3783 indent_delta.chars().collect::<String>(),
3784 ));
3785
3786 // Update this selection's endpoints to reflect the indentation.
3787 if row == selection.start.row {
3788 selection.start.column += indent_delta.len;
3789 }
3790 if row == selection.end.row {
3791 selection.end.column += indent_delta.len;
3792 delta_for_end_row = indent_delta.len;
3793 }
3794 }
3795
3796 if selection.start.row == selection.end.row {
3797 delta_for_start_row + delta_for_end_row
3798 } else {
3799 delta_for_end_row
3800 }
3801 }
3802
3803 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
3804 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3805 let selections = self.selections.all::<Point>(cx);
3806 let mut deletion_ranges = Vec::new();
3807 let mut last_outdent = None;
3808 {
3809 let buffer = self.buffer.read(cx);
3810 let snapshot = buffer.snapshot(cx);
3811 for selection in &selections {
3812 let settings = buffer.settings_at(selection.start, cx);
3813 let tab_size = settings.tab_size.get();
3814 let mut rows = selection.spanned_rows(false, &display_map);
3815
3816 // Avoid re-outdenting a row that has already been outdented by a
3817 // previous selection.
3818 if let Some(last_row) = last_outdent {
3819 if last_row == rows.start {
3820 rows.start += 1;
3821 }
3822 }
3823
3824 for row in rows {
3825 let indent_size = snapshot.indent_size_for_line(row);
3826 if indent_size.len > 0 {
3827 let deletion_len = match indent_size.kind {
3828 IndentKind::Space => {
3829 let columns_to_prev_tab_stop = indent_size.len % tab_size;
3830 if columns_to_prev_tab_stop == 0 {
3831 tab_size
3832 } else {
3833 columns_to_prev_tab_stop
3834 }
3835 }
3836 IndentKind::Tab => 1,
3837 };
3838 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3839 last_outdent = Some(row);
3840 }
3841 }
3842 }
3843 }
3844
3845 self.transact(cx, |this, cx| {
3846 this.buffer.update(cx, |buffer, cx| {
3847 let empty_str: Arc<str> = "".into();
3848 buffer.edit(
3849 deletion_ranges
3850 .into_iter()
3851 .map(|range| (range, empty_str.clone())),
3852 None,
3853 cx,
3854 );
3855 });
3856 let selections = this.selections.all::<usize>(cx);
3857 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3858 });
3859 }
3860
3861 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3862 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3863 let selections = self.selections.all::<Point>(cx);
3864
3865 let mut new_cursors = Vec::new();
3866 let mut edit_ranges = Vec::new();
3867 let mut selections = selections.iter().peekable();
3868 while let Some(selection) = selections.next() {
3869 let mut rows = selection.spanned_rows(false, &display_map);
3870 let goal_display_column = selection.head().to_display_point(&display_map).column();
3871
3872 // Accumulate contiguous regions of rows that we want to delete.
3873 while let Some(next_selection) = selections.peek() {
3874 let next_rows = next_selection.spanned_rows(false, &display_map);
3875 if next_rows.start <= rows.end {
3876 rows.end = next_rows.end;
3877 selections.next().unwrap();
3878 } else {
3879 break;
3880 }
3881 }
3882
3883 let buffer = &display_map.buffer_snapshot;
3884 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
3885 let edit_end;
3886 let cursor_buffer_row;
3887 if buffer.max_point().row >= rows.end {
3888 // If there's a line after the range, delete the \n from the end of the row range
3889 // and position the cursor on the next line.
3890 edit_end = Point::new(rows.end, 0).to_offset(buffer);
3891 cursor_buffer_row = rows.end;
3892 } else {
3893 // If there isn't a line after the range, delete the \n from the line before the
3894 // start of the row range and position the cursor there.
3895 edit_start = edit_start.saturating_sub(1);
3896 edit_end = buffer.len();
3897 cursor_buffer_row = rows.start.saturating_sub(1);
3898 }
3899
3900 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3901 *cursor.column_mut() =
3902 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3903
3904 new_cursors.push((
3905 selection.id,
3906 buffer.anchor_after(cursor.to_point(&display_map)),
3907 ));
3908 edit_ranges.push(edit_start..edit_end);
3909 }
3910
3911 self.transact(cx, |this, cx| {
3912 let buffer = this.buffer.update(cx, |buffer, cx| {
3913 let empty_str: Arc<str> = "".into();
3914 buffer.edit(
3915 edit_ranges
3916 .into_iter()
3917 .map(|range| (range, empty_str.clone())),
3918 None,
3919 cx,
3920 );
3921 buffer.snapshot(cx)
3922 });
3923 let new_selections = new_cursors
3924 .into_iter()
3925 .map(|(id, cursor)| {
3926 let cursor = cursor.to_point(&buffer);
3927 Selection {
3928 id,
3929 start: cursor,
3930 end: cursor,
3931 reversed: false,
3932 goal: SelectionGoal::None,
3933 }
3934 })
3935 .collect();
3936
3937 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3938 s.select(new_selections);
3939 });
3940 });
3941 }
3942
3943 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3944 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3945 let buffer = &display_map.buffer_snapshot;
3946 let selections = self.selections.all::<Point>(cx);
3947
3948 let mut edits = Vec::new();
3949 let mut selections_iter = selections.iter().peekable();
3950 while let Some(selection) = selections_iter.next() {
3951 // Avoid duplicating the same lines twice.
3952 let mut rows = selection.spanned_rows(false, &display_map);
3953
3954 while let Some(next_selection) = selections_iter.peek() {
3955 let next_rows = next_selection.spanned_rows(false, &display_map);
3956 if next_rows.start < rows.end {
3957 rows.end = next_rows.end;
3958 selections_iter.next().unwrap();
3959 } else {
3960 break;
3961 }
3962 }
3963
3964 // Copy the text from the selected row region and splice it at the start of the region.
3965 let start = Point::new(rows.start, 0);
3966 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3967 let text = buffer
3968 .text_for_range(start..end)
3969 .chain(Some("\n"))
3970 .collect::<String>();
3971 edits.push((start..start, text));
3972 }
3973
3974 self.transact(cx, |this, cx| {
3975 this.buffer.update(cx, |buffer, cx| {
3976 buffer.edit(edits, None, cx);
3977 });
3978
3979 this.request_autoscroll(Autoscroll::fit(), cx);
3980 });
3981 }
3982
3983 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3984 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3985 let buffer = self.buffer.read(cx).snapshot(cx);
3986
3987 let mut edits = Vec::new();
3988 let mut unfold_ranges = Vec::new();
3989 let mut refold_ranges = Vec::new();
3990
3991 let selections = self.selections.all::<Point>(cx);
3992 let mut selections = selections.iter().peekable();
3993 let mut contiguous_row_selections = Vec::new();
3994 let mut new_selections = Vec::new();
3995
3996 while let Some(selection) = selections.next() {
3997 // Find all the selections that span a contiguous row range
3998 let (start_row, end_row) = consume_contiguous_rows(
3999 &mut contiguous_row_selections,
4000 selection,
4001 &display_map,
4002 &mut selections,
4003 );
4004
4005 // Move the text spanned by the row range to be before the line preceding the row range
4006 if start_row > 0 {
4007 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4008 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4009 let insertion_point = display_map
4010 .prev_line_boundary(Point::new(start_row - 1, 0))
4011 .0;
4012
4013 // Don't move lines across excerpts
4014 if buffer
4015 .excerpt_boundaries_in_range((
4016 Bound::Excluded(insertion_point),
4017 Bound::Included(range_to_move.end),
4018 ))
4019 .next()
4020 .is_none()
4021 {
4022 let text = buffer
4023 .text_for_range(range_to_move.clone())
4024 .flat_map(|s| s.chars())
4025 .skip(1)
4026 .chain(['\n'])
4027 .collect::<String>();
4028
4029 edits.push((
4030 buffer.anchor_after(range_to_move.start)
4031 ..buffer.anchor_before(range_to_move.end),
4032 String::new(),
4033 ));
4034 let insertion_anchor = buffer.anchor_after(insertion_point);
4035 edits.push((insertion_anchor..insertion_anchor, text));
4036
4037 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4038
4039 // Move selections up
4040 new_selections.extend(contiguous_row_selections.drain(..).map(
4041 |mut selection| {
4042 selection.start.row -= row_delta;
4043 selection.end.row -= row_delta;
4044 selection
4045 },
4046 ));
4047
4048 // Move folds up
4049 unfold_ranges.push(range_to_move.clone());
4050 for fold in display_map.folds_in_range(
4051 buffer.anchor_before(range_to_move.start)
4052 ..buffer.anchor_after(range_to_move.end),
4053 ) {
4054 let mut start = fold.start.to_point(&buffer);
4055 let mut end = fold.end.to_point(&buffer);
4056 start.row -= row_delta;
4057 end.row -= row_delta;
4058 refold_ranges.push(start..end);
4059 }
4060 }
4061 }
4062
4063 // If we didn't move line(s), preserve the existing selections
4064 new_selections.append(&mut contiguous_row_selections);
4065 }
4066
4067 self.transact(cx, |this, cx| {
4068 this.unfold_ranges(unfold_ranges, true, true, cx);
4069 this.buffer.update(cx, |buffer, cx| {
4070 for (range, text) in edits {
4071 buffer.edit([(range, text)], None, cx);
4072 }
4073 });
4074 this.fold_ranges(refold_ranges, true, cx);
4075 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4076 s.select(new_selections);
4077 })
4078 });
4079 }
4080
4081 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4082 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4083 let buffer = self.buffer.read(cx).snapshot(cx);
4084
4085 let mut edits = Vec::new();
4086 let mut unfold_ranges = Vec::new();
4087 let mut refold_ranges = Vec::new();
4088
4089 let selections = self.selections.all::<Point>(cx);
4090 let mut selections = selections.iter().peekable();
4091 let mut contiguous_row_selections = Vec::new();
4092 let mut new_selections = Vec::new();
4093
4094 while let Some(selection) = selections.next() {
4095 // Find all the selections that span a contiguous row range
4096 let (start_row, end_row) = consume_contiguous_rows(
4097 &mut contiguous_row_selections,
4098 selection,
4099 &display_map,
4100 &mut selections,
4101 );
4102
4103 // Move the text spanned by the row range to be after the last line of the row range
4104 if end_row <= buffer.max_point().row {
4105 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4106 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4107
4108 // Don't move lines across excerpt boundaries
4109 if buffer
4110 .excerpt_boundaries_in_range((
4111 Bound::Excluded(range_to_move.start),
4112 Bound::Included(insertion_point),
4113 ))
4114 .next()
4115 .is_none()
4116 {
4117 let mut text = String::from("\n");
4118 text.extend(buffer.text_for_range(range_to_move.clone()));
4119 text.pop(); // Drop trailing newline
4120 edits.push((
4121 buffer.anchor_after(range_to_move.start)
4122 ..buffer.anchor_before(range_to_move.end),
4123 String::new(),
4124 ));
4125 let insertion_anchor = buffer.anchor_after(insertion_point);
4126 edits.push((insertion_anchor..insertion_anchor, text));
4127
4128 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4129
4130 // Move selections down
4131 new_selections.extend(contiguous_row_selections.drain(..).map(
4132 |mut selection| {
4133 selection.start.row += row_delta;
4134 selection.end.row += row_delta;
4135 selection
4136 },
4137 ));
4138
4139 // Move folds down
4140 unfold_ranges.push(range_to_move.clone());
4141 for fold in display_map.folds_in_range(
4142 buffer.anchor_before(range_to_move.start)
4143 ..buffer.anchor_after(range_to_move.end),
4144 ) {
4145 let mut start = fold.start.to_point(&buffer);
4146 let mut end = fold.end.to_point(&buffer);
4147 start.row += row_delta;
4148 end.row += row_delta;
4149 refold_ranges.push(start..end);
4150 }
4151 }
4152 }
4153
4154 // If we didn't move line(s), preserve the existing selections
4155 new_selections.append(&mut contiguous_row_selections);
4156 }
4157
4158 self.transact(cx, |this, cx| {
4159 this.unfold_ranges(unfold_ranges, true, true, cx);
4160 this.buffer.update(cx, |buffer, cx| {
4161 for (range, text) in edits {
4162 buffer.edit([(range, text)], None, cx);
4163 }
4164 });
4165 this.fold_ranges(refold_ranges, true, cx);
4166 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4167 });
4168 }
4169
4170 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4171 self.transact(cx, |this, cx| {
4172 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4173 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4174 let line_mode = s.line_mode;
4175 s.move_with(|display_map, selection| {
4176 if !selection.is_empty() || line_mode {
4177 return;
4178 }
4179
4180 let mut head = selection.head();
4181 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4182 if head.column() == display_map.line_len(head.row()) {
4183 transpose_offset = display_map
4184 .buffer_snapshot
4185 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4186 }
4187
4188 if transpose_offset == 0 {
4189 return;
4190 }
4191
4192 *head.column_mut() += 1;
4193 head = display_map.clip_point(head, Bias::Right);
4194 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4195
4196 let transpose_start = display_map
4197 .buffer_snapshot
4198 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4199 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4200 let transpose_end = display_map
4201 .buffer_snapshot
4202 .clip_offset(transpose_offset + 1, Bias::Right);
4203 if let Some(ch) =
4204 display_map.buffer_snapshot.chars_at(transpose_start).next()
4205 {
4206 edits.push((transpose_start..transpose_offset, String::new()));
4207 edits.push((transpose_end..transpose_end, ch.to_string()));
4208 }
4209 }
4210 });
4211 edits
4212 });
4213 this.buffer
4214 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4215 let selections = this.selections.all::<usize>(cx);
4216 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4217 s.select(selections);
4218 });
4219 });
4220 }
4221
4222 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4223 let mut text = String::new();
4224 let buffer = self.buffer.read(cx).snapshot(cx);
4225 let mut selections = self.selections.all::<Point>(cx);
4226 let mut clipboard_selections = Vec::with_capacity(selections.len());
4227 {
4228 let max_point = buffer.max_point();
4229 for selection in &mut selections {
4230 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4231 if is_entire_line {
4232 selection.start = Point::new(selection.start.row, 0);
4233 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4234 selection.goal = SelectionGoal::None;
4235 }
4236 let mut len = 0;
4237 for chunk in buffer.text_for_range(selection.start..selection.end) {
4238 text.push_str(chunk);
4239 len += chunk.len();
4240 }
4241 clipboard_selections.push(ClipboardSelection {
4242 len,
4243 is_entire_line,
4244 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4245 });
4246 }
4247 }
4248
4249 self.transact(cx, |this, cx| {
4250 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4251 s.select(selections);
4252 });
4253 this.insert("", cx);
4254 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4255 });
4256 }
4257
4258 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4259 let selections = self.selections.all::<Point>(cx);
4260 let buffer = self.buffer.read(cx).read(cx);
4261 let mut text = String::new();
4262
4263 let mut clipboard_selections = Vec::with_capacity(selections.len());
4264 {
4265 let max_point = buffer.max_point();
4266 for selection in selections.iter() {
4267 let mut start = selection.start;
4268 let mut end = selection.end;
4269 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4270 if is_entire_line {
4271 start = Point::new(start.row, 0);
4272 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4273 }
4274 let mut len = 0;
4275 for chunk in buffer.text_for_range(start..end) {
4276 text.push_str(chunk);
4277 len += chunk.len();
4278 }
4279 clipboard_selections.push(ClipboardSelection {
4280 len,
4281 is_entire_line,
4282 first_line_indent: buffer.indent_size_for_line(start.row).len,
4283 });
4284 }
4285 }
4286
4287 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4288 }
4289
4290 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4291 self.transact(cx, |this, cx| {
4292 if let Some(item) = cx.read_from_clipboard() {
4293 let mut clipboard_text = Cow::Borrowed(item.text());
4294 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4295 let old_selections = this.selections.all::<usize>(cx);
4296 let all_selections_were_entire_line =
4297 clipboard_selections.iter().all(|s| s.is_entire_line);
4298 let first_selection_indent_column =
4299 clipboard_selections.first().map(|s| s.first_line_indent);
4300 if clipboard_selections.len() != old_selections.len() {
4301 let mut newline_separated_text = String::new();
4302 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4303 let mut ix = 0;
4304 while let Some(clipboard_selection) = clipboard_selections.next() {
4305 newline_separated_text
4306 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4307 ix += clipboard_selection.len;
4308 if clipboard_selections.peek().is_some() {
4309 newline_separated_text.push('\n');
4310 }
4311 }
4312 clipboard_text = Cow::Owned(newline_separated_text);
4313 }
4314
4315 this.buffer.update(cx, |buffer, cx| {
4316 let snapshot = buffer.read(cx);
4317 let mut start_offset = 0;
4318 let mut edits = Vec::new();
4319 let mut original_indent_columns = Vec::new();
4320 let line_mode = this.selections.line_mode;
4321 for (ix, selection) in old_selections.iter().enumerate() {
4322 let to_insert;
4323 let entire_line;
4324 let original_indent_column;
4325 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4326 let end_offset = start_offset + clipboard_selection.len;
4327 to_insert = &clipboard_text[start_offset..end_offset];
4328 entire_line = clipboard_selection.is_entire_line;
4329 start_offset = end_offset;
4330 original_indent_column =
4331 Some(clipboard_selection.first_line_indent);
4332 } else {
4333 to_insert = clipboard_text.as_str();
4334 entire_line = all_selections_were_entire_line;
4335 original_indent_column = first_selection_indent_column
4336 }
4337
4338 // If the corresponding selection was empty when this slice of the
4339 // clipboard text was written, then the entire line containing the
4340 // selection was copied. If this selection is also currently empty,
4341 // then paste the line before the current line of the buffer.
4342 let range = if selection.is_empty() && !line_mode && entire_line {
4343 let column = selection.start.to_point(&snapshot).column as usize;
4344 let line_start = selection.start - column;
4345 line_start..line_start
4346 } else {
4347 selection.range()
4348 };
4349
4350 edits.push((range, to_insert));
4351 original_indent_columns.extend(original_indent_column);
4352 }
4353 drop(snapshot);
4354
4355 buffer.edit(
4356 edits,
4357 Some(AutoindentMode::Block {
4358 original_indent_columns,
4359 }),
4360 cx,
4361 );
4362 });
4363
4364 let selections = this.selections.all::<usize>(cx);
4365 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4366 } else {
4367 this.insert(&clipboard_text, cx);
4368 }
4369 }
4370 });
4371 }
4372
4373 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4374 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4375 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4376 self.change_selections(None, cx, |s| {
4377 s.select_anchors(selections.to_vec());
4378 });
4379 }
4380 self.request_autoscroll(Autoscroll::fit(), cx);
4381 self.unmark_text(cx);
4382 self.refresh_copilot_suggestions(true, cx);
4383 cx.emit(Event::Edited);
4384 }
4385 }
4386
4387 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4388 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4389 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4390 {
4391 self.change_selections(None, cx, |s| {
4392 s.select_anchors(selections.to_vec());
4393 });
4394 }
4395 self.request_autoscroll(Autoscroll::fit(), cx);
4396 self.unmark_text(cx);
4397 self.refresh_copilot_suggestions(true, cx);
4398 cx.emit(Event::Edited);
4399 }
4400 }
4401
4402 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4403 self.buffer
4404 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4405 }
4406
4407 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4408 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4409 let line_mode = s.line_mode;
4410 s.move_with(|map, selection| {
4411 let cursor = if selection.is_empty() && !line_mode {
4412 movement::left(map, selection.start)
4413 } else {
4414 selection.start
4415 };
4416 selection.collapse_to(cursor, SelectionGoal::None);
4417 });
4418 })
4419 }
4420
4421 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4422 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4423 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4424 })
4425 }
4426
4427 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4428 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4429 let line_mode = s.line_mode;
4430 s.move_with(|map, selection| {
4431 let cursor = if selection.is_empty() && !line_mode {
4432 movement::right(map, selection.end)
4433 } else {
4434 selection.end
4435 };
4436 selection.collapse_to(cursor, SelectionGoal::None)
4437 });
4438 })
4439 }
4440
4441 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4443 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4444 })
4445 }
4446
4447 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4448 if self.take_rename(true, cx).is_some() {
4449 return;
4450 }
4451
4452 if let Some(context_menu) = self.context_menu.as_mut() {
4453 if context_menu.select_prev(cx) {
4454 return;
4455 }
4456 }
4457
4458 if matches!(self.mode, EditorMode::SingleLine) {
4459 cx.propagate_action();
4460 return;
4461 }
4462
4463 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4464 let line_mode = s.line_mode;
4465 s.move_with(|map, selection| {
4466 if !selection.is_empty() && !line_mode {
4467 selection.goal = SelectionGoal::None;
4468 }
4469 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4470 selection.collapse_to(cursor, goal);
4471 });
4472 })
4473 }
4474
4475 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4476 if self.take_rename(true, cx).is_some() {
4477 return;
4478 }
4479
4480 if self
4481 .context_menu
4482 .as_mut()
4483 .map(|menu| menu.select_first(cx))
4484 .unwrap_or(false)
4485 {
4486 return;
4487 }
4488
4489 if matches!(self.mode, EditorMode::SingleLine) {
4490 cx.propagate_action();
4491 return;
4492 }
4493
4494 let row_count = if let Some(row_count) = self.visible_line_count() {
4495 row_count as u32 - 1
4496 } else {
4497 return;
4498 };
4499
4500 let autoscroll = if action.center_cursor {
4501 Autoscroll::center()
4502 } else {
4503 Autoscroll::fit()
4504 };
4505
4506 self.change_selections(Some(autoscroll), cx, |s| {
4507 let line_mode = s.line_mode;
4508 s.move_with(|map, selection| {
4509 if !selection.is_empty() && !line_mode {
4510 selection.goal = SelectionGoal::None;
4511 }
4512 let (cursor, goal) =
4513 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4514 selection.collapse_to(cursor, goal);
4515 });
4516 });
4517 }
4518
4519 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
4520 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4521 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
4522 })
4523 }
4524
4525 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
4526 self.take_rename(true, cx);
4527
4528 if let Some(context_menu) = self.context_menu.as_mut() {
4529 if context_menu.select_next(cx) {
4530 return;
4531 }
4532 }
4533
4534 if self.mode == EditorMode::SingleLine {
4535 cx.propagate_action();
4536 return;
4537 }
4538
4539 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4540 let line_mode = s.line_mode;
4541 s.move_with(|map, selection| {
4542 if !selection.is_empty() && !line_mode {
4543 selection.goal = SelectionGoal::None;
4544 }
4545 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
4546 selection.collapse_to(cursor, goal);
4547 });
4548 });
4549 }
4550
4551 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
4552 if self.take_rename(true, cx).is_some() {
4553 return;
4554 }
4555
4556 if self
4557 .context_menu
4558 .as_mut()
4559 .map(|menu| menu.select_last(cx))
4560 .unwrap_or(false)
4561 {
4562 return;
4563 }
4564
4565 if matches!(self.mode, EditorMode::SingleLine) {
4566 cx.propagate_action();
4567 return;
4568 }
4569
4570 let row_count = if let Some(row_count) = self.visible_line_count() {
4571 row_count as u32 - 1
4572 } else {
4573 return;
4574 };
4575
4576 let autoscroll = if action.center_cursor {
4577 Autoscroll::center()
4578 } else {
4579 Autoscroll::fit()
4580 };
4581
4582 self.change_selections(Some(autoscroll), cx, |s| {
4583 let line_mode = s.line_mode;
4584 s.move_with(|map, selection| {
4585 if !selection.is_empty() && !line_mode {
4586 selection.goal = SelectionGoal::None;
4587 }
4588 let (cursor, goal) =
4589 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
4590 selection.collapse_to(cursor, goal);
4591 });
4592 });
4593 }
4594
4595 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
4596 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4597 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
4598 });
4599 }
4600
4601 pub fn move_to_previous_word_start(
4602 &mut self,
4603 _: &MoveToPreviousWordStart,
4604 cx: &mut ViewContext<Self>,
4605 ) {
4606 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4607 s.move_cursors_with(|map, head, _| {
4608 (
4609 movement::previous_word_start(map, head),
4610 SelectionGoal::None,
4611 )
4612 });
4613 })
4614 }
4615
4616 pub fn move_to_previous_subword_start(
4617 &mut self,
4618 _: &MoveToPreviousSubwordStart,
4619 cx: &mut ViewContext<Self>,
4620 ) {
4621 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4622 s.move_cursors_with(|map, head, _| {
4623 (
4624 movement::previous_subword_start(map, head),
4625 SelectionGoal::None,
4626 )
4627 });
4628 })
4629 }
4630
4631 pub fn select_to_previous_word_start(
4632 &mut self,
4633 _: &SelectToPreviousWordStart,
4634 cx: &mut ViewContext<Self>,
4635 ) {
4636 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4637 s.move_heads_with(|map, head, _| {
4638 (
4639 movement::previous_word_start(map, head),
4640 SelectionGoal::None,
4641 )
4642 });
4643 })
4644 }
4645
4646 pub fn select_to_previous_subword_start(
4647 &mut self,
4648 _: &SelectToPreviousSubwordStart,
4649 cx: &mut ViewContext<Self>,
4650 ) {
4651 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4652 s.move_heads_with(|map, head, _| {
4653 (
4654 movement::previous_subword_start(map, head),
4655 SelectionGoal::None,
4656 )
4657 });
4658 })
4659 }
4660
4661 pub fn delete_to_previous_word_start(
4662 &mut self,
4663 _: &DeleteToPreviousWordStart,
4664 cx: &mut ViewContext<Self>,
4665 ) {
4666 self.transact(cx, |this, cx| {
4667 this.select_autoclose_pair(cx);
4668 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4669 let line_mode = s.line_mode;
4670 s.move_with(|map, selection| {
4671 if selection.is_empty() && !line_mode {
4672 let cursor = movement::previous_word_start(map, selection.head());
4673 selection.set_head(cursor, SelectionGoal::None);
4674 }
4675 });
4676 });
4677 this.insert("", cx);
4678 });
4679 }
4680
4681 pub fn delete_to_previous_subword_start(
4682 &mut self,
4683 _: &DeleteToPreviousSubwordStart,
4684 cx: &mut ViewContext<Self>,
4685 ) {
4686 self.transact(cx, |this, cx| {
4687 this.select_autoclose_pair(cx);
4688 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4689 let line_mode = s.line_mode;
4690 s.move_with(|map, selection| {
4691 if selection.is_empty() && !line_mode {
4692 let cursor = movement::previous_subword_start(map, selection.head());
4693 selection.set_head(cursor, SelectionGoal::None);
4694 }
4695 });
4696 });
4697 this.insert("", cx);
4698 });
4699 }
4700
4701 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
4702 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4703 s.move_cursors_with(|map, head, _| {
4704 (movement::next_word_end(map, head), SelectionGoal::None)
4705 });
4706 })
4707 }
4708
4709 pub fn move_to_next_subword_end(
4710 &mut self,
4711 _: &MoveToNextSubwordEnd,
4712 cx: &mut ViewContext<Self>,
4713 ) {
4714 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4715 s.move_cursors_with(|map, head, _| {
4716 (movement::next_subword_end(map, head), SelectionGoal::None)
4717 });
4718 })
4719 }
4720
4721 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
4722 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4723 s.move_heads_with(|map, head, _| {
4724 (movement::next_word_end(map, head), SelectionGoal::None)
4725 });
4726 })
4727 }
4728
4729 pub fn select_to_next_subword_end(
4730 &mut self,
4731 _: &SelectToNextSubwordEnd,
4732 cx: &mut ViewContext<Self>,
4733 ) {
4734 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4735 s.move_heads_with(|map, head, _| {
4736 (movement::next_subword_end(map, head), SelectionGoal::None)
4737 });
4738 })
4739 }
4740
4741 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
4742 self.transact(cx, |this, cx| {
4743 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4744 let line_mode = s.line_mode;
4745 s.move_with(|map, selection| {
4746 if selection.is_empty() && !line_mode {
4747 let cursor = movement::next_word_end(map, selection.head());
4748 selection.set_head(cursor, SelectionGoal::None);
4749 }
4750 });
4751 });
4752 this.insert("", cx);
4753 });
4754 }
4755
4756 pub fn delete_to_next_subword_end(
4757 &mut self,
4758 _: &DeleteToNextSubwordEnd,
4759 cx: &mut ViewContext<Self>,
4760 ) {
4761 self.transact(cx, |this, cx| {
4762 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4763 s.move_with(|map, selection| {
4764 if selection.is_empty() {
4765 let cursor = movement::next_subword_end(map, selection.head());
4766 selection.set_head(cursor, SelectionGoal::None);
4767 }
4768 });
4769 });
4770 this.insert("", cx);
4771 });
4772 }
4773
4774 pub fn move_to_beginning_of_line(
4775 &mut self,
4776 _: &MoveToBeginningOfLine,
4777 cx: &mut ViewContext<Self>,
4778 ) {
4779 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4780 s.move_cursors_with(|map, head, _| {
4781 (
4782 movement::indented_line_beginning(map, head, true),
4783 SelectionGoal::None,
4784 )
4785 });
4786 })
4787 }
4788
4789 pub fn select_to_beginning_of_line(
4790 &mut self,
4791 action: &SelectToBeginningOfLine,
4792 cx: &mut ViewContext<Self>,
4793 ) {
4794 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4795 s.move_heads_with(|map, head, _| {
4796 (
4797 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
4798 SelectionGoal::None,
4799 )
4800 });
4801 });
4802 }
4803
4804 pub fn delete_to_beginning_of_line(
4805 &mut self,
4806 _: &DeleteToBeginningOfLine,
4807 cx: &mut ViewContext<Self>,
4808 ) {
4809 self.transact(cx, |this, cx| {
4810 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4811 s.move_with(|_, selection| {
4812 selection.reversed = true;
4813 });
4814 });
4815
4816 this.select_to_beginning_of_line(
4817 &SelectToBeginningOfLine {
4818 stop_at_soft_wraps: false,
4819 },
4820 cx,
4821 );
4822 this.backspace(&Backspace, cx);
4823 });
4824 }
4825
4826 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
4827 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4828 s.move_cursors_with(|map, head, _| {
4829 (movement::line_end(map, head, true), SelectionGoal::None)
4830 });
4831 })
4832 }
4833
4834 pub fn select_to_end_of_line(
4835 &mut self,
4836 action: &SelectToEndOfLine,
4837 cx: &mut ViewContext<Self>,
4838 ) {
4839 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4840 s.move_heads_with(|map, head, _| {
4841 (
4842 movement::line_end(map, head, action.stop_at_soft_wraps),
4843 SelectionGoal::None,
4844 )
4845 });
4846 })
4847 }
4848
4849 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
4850 self.transact(cx, |this, cx| {
4851 this.select_to_end_of_line(
4852 &SelectToEndOfLine {
4853 stop_at_soft_wraps: false,
4854 },
4855 cx,
4856 );
4857 this.delete(&Delete, cx);
4858 });
4859 }
4860
4861 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, 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.cut(&Cut, cx);
4870 });
4871 }
4872
4873 pub fn move_to_start_of_paragraph(
4874 &mut self,
4875 _: &MoveToStartOfParagraph,
4876 cx: &mut ViewContext<Self>,
4877 ) {
4878 if matches!(self.mode, EditorMode::SingleLine) {
4879 cx.propagate_action();
4880 return;
4881 }
4882
4883 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4884 s.move_with(|map, selection| {
4885 selection.collapse_to(
4886 movement::start_of_paragraph(map, selection.head()),
4887 SelectionGoal::None,
4888 )
4889 });
4890 })
4891 }
4892
4893 pub fn move_to_end_of_paragraph(
4894 &mut self,
4895 _: &MoveToEndOfParagraph,
4896 cx: &mut ViewContext<Self>,
4897 ) {
4898 if matches!(self.mode, EditorMode::SingleLine) {
4899 cx.propagate_action();
4900 return;
4901 }
4902
4903 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4904 s.move_with(|map, selection| {
4905 selection.collapse_to(
4906 movement::end_of_paragraph(map, selection.head()),
4907 SelectionGoal::None,
4908 )
4909 });
4910 })
4911 }
4912
4913 pub fn select_to_start_of_paragraph(
4914 &mut self,
4915 _: &SelectToStartOfParagraph,
4916 cx: &mut ViewContext<Self>,
4917 ) {
4918 if matches!(self.mode, EditorMode::SingleLine) {
4919 cx.propagate_action();
4920 return;
4921 }
4922
4923 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4924 s.move_heads_with(|map, head, _| {
4925 (movement::start_of_paragraph(map, head), SelectionGoal::None)
4926 });
4927 })
4928 }
4929
4930 pub fn select_to_end_of_paragraph(
4931 &mut self,
4932 _: &SelectToEndOfParagraph,
4933 cx: &mut ViewContext<Self>,
4934 ) {
4935 if matches!(self.mode, EditorMode::SingleLine) {
4936 cx.propagate_action();
4937 return;
4938 }
4939
4940 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4941 s.move_heads_with(|map, head, _| {
4942 (movement::end_of_paragraph(map, head), SelectionGoal::None)
4943 });
4944 })
4945 }
4946
4947 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
4948 if matches!(self.mode, EditorMode::SingleLine) {
4949 cx.propagate_action();
4950 return;
4951 }
4952
4953 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4954 s.select_ranges(vec![0..0]);
4955 });
4956 }
4957
4958 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
4959 let mut selection = self.selections.last::<Point>(cx);
4960 selection.set_head(Point::zero(), SelectionGoal::None);
4961
4962 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4963 s.select(vec![selection]);
4964 });
4965 }
4966
4967 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
4968 if matches!(self.mode, EditorMode::SingleLine) {
4969 cx.propagate_action();
4970 return;
4971 }
4972
4973 let cursor = self.buffer.read(cx).read(cx).len();
4974 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4975 s.select_ranges(vec![cursor..cursor])
4976 });
4977 }
4978
4979 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
4980 self.nav_history = nav_history;
4981 }
4982
4983 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4984 self.nav_history.as_ref()
4985 }
4986
4987 fn push_to_nav_history(
4988 &mut self,
4989 cursor_anchor: Anchor,
4990 new_position: Option<Point>,
4991 cx: &mut ViewContext<Self>,
4992 ) {
4993 if let Some(nav_history) = self.nav_history.as_mut() {
4994 let buffer = self.buffer.read(cx).read(cx);
4995 let cursor_position = cursor_anchor.to_point(&buffer);
4996 let scroll_state = self.scroll_manager.anchor();
4997 let scroll_top_row = scroll_state.top_row(&buffer);
4998 drop(buffer);
4999
5000 if let Some(new_position) = new_position {
5001 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5002 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5003 return;
5004 }
5005 }
5006
5007 nav_history.push(
5008 Some(NavigationData {
5009 cursor_anchor,
5010 cursor_position,
5011 scroll_anchor: scroll_state,
5012 scroll_top_row,
5013 }),
5014 cx,
5015 );
5016 }
5017 }
5018
5019 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5020 let buffer = self.buffer.read(cx).snapshot(cx);
5021 let mut selection = self.selections.first::<usize>(cx);
5022 selection.set_head(buffer.len(), SelectionGoal::None);
5023 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5024 s.select(vec![selection]);
5025 });
5026 }
5027
5028 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5029 let end = self.buffer.read(cx).read(cx).len();
5030 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5031 s.select_ranges(vec![0..end]);
5032 });
5033 }
5034
5035 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5036 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5037 let mut selections = self.selections.all::<Point>(cx);
5038 let max_point = display_map.buffer_snapshot.max_point();
5039 for selection in &mut selections {
5040 let rows = selection.spanned_rows(true, &display_map);
5041 selection.start = Point::new(rows.start, 0);
5042 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5043 selection.reversed = false;
5044 }
5045 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5046 s.select(selections);
5047 });
5048 }
5049
5050 pub fn split_selection_into_lines(
5051 &mut self,
5052 _: &SplitSelectionIntoLines,
5053 cx: &mut ViewContext<Self>,
5054 ) {
5055 let mut to_unfold = Vec::new();
5056 let mut new_selection_ranges = Vec::new();
5057 {
5058 let selections = self.selections.all::<Point>(cx);
5059 let buffer = self.buffer.read(cx).read(cx);
5060 for selection in selections {
5061 for row in selection.start.row..selection.end.row {
5062 let cursor = Point::new(row, buffer.line_len(row));
5063 new_selection_ranges.push(cursor..cursor);
5064 }
5065 new_selection_ranges.push(selection.end..selection.end);
5066 to_unfold.push(selection.start..selection.end);
5067 }
5068 }
5069 self.unfold_ranges(to_unfold, true, true, cx);
5070 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5071 s.select_ranges(new_selection_ranges);
5072 });
5073 }
5074
5075 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5076 self.add_selection(true, cx);
5077 }
5078
5079 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5080 self.add_selection(false, cx);
5081 }
5082
5083 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5084 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5085 let mut selections = self.selections.all::<Point>(cx);
5086 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5087 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5088 let range = oldest_selection.display_range(&display_map).sorted();
5089 let columns = cmp::min(range.start.column(), range.end.column())
5090 ..cmp::max(range.start.column(), range.end.column());
5091
5092 selections.clear();
5093 let mut stack = Vec::new();
5094 for row in range.start.row()..=range.end.row() {
5095 if let Some(selection) = self.selections.build_columnar_selection(
5096 &display_map,
5097 row,
5098 &columns,
5099 oldest_selection.reversed,
5100 ) {
5101 stack.push(selection.id);
5102 selections.push(selection);
5103 }
5104 }
5105
5106 if above {
5107 stack.reverse();
5108 }
5109
5110 AddSelectionsState { above, stack }
5111 });
5112
5113 let last_added_selection = *state.stack.last().unwrap();
5114 let mut new_selections = Vec::new();
5115 if above == state.above {
5116 let end_row = if above {
5117 0
5118 } else {
5119 display_map.max_point().row()
5120 };
5121
5122 'outer: for selection in selections {
5123 if selection.id == last_added_selection {
5124 let range = selection.display_range(&display_map).sorted();
5125 debug_assert_eq!(range.start.row(), range.end.row());
5126 let mut row = range.start.row();
5127 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5128 {
5129 start..end
5130 } else {
5131 cmp::min(range.start.column(), range.end.column())
5132 ..cmp::max(range.start.column(), range.end.column())
5133 };
5134
5135 while row != end_row {
5136 if above {
5137 row -= 1;
5138 } else {
5139 row += 1;
5140 }
5141
5142 if let Some(new_selection) = self.selections.build_columnar_selection(
5143 &display_map,
5144 row,
5145 &columns,
5146 selection.reversed,
5147 ) {
5148 state.stack.push(new_selection.id);
5149 if above {
5150 new_selections.push(new_selection);
5151 new_selections.push(selection);
5152 } else {
5153 new_selections.push(selection);
5154 new_selections.push(new_selection);
5155 }
5156
5157 continue 'outer;
5158 }
5159 }
5160 }
5161
5162 new_selections.push(selection);
5163 }
5164 } else {
5165 new_selections = selections;
5166 new_selections.retain(|s| s.id != last_added_selection);
5167 state.stack.pop();
5168 }
5169
5170 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5171 s.select(new_selections);
5172 });
5173 if state.stack.len() > 1 {
5174 self.add_selections_state = Some(state);
5175 }
5176 }
5177
5178 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5179 self.push_to_selection_history();
5180 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5181 let buffer = &display_map.buffer_snapshot;
5182 let mut selections = self.selections.all::<usize>(cx);
5183 if let Some(mut select_next_state) = self.select_next_state.take() {
5184 let query = &select_next_state.query;
5185 if !select_next_state.done {
5186 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5187 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5188 let mut next_selected_range = None;
5189
5190 let bytes_after_last_selection =
5191 buffer.bytes_in_range(last_selection.end..buffer.len());
5192 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5193 let query_matches = query
5194 .stream_find_iter(bytes_after_last_selection)
5195 .map(|result| (last_selection.end, result))
5196 .chain(
5197 query
5198 .stream_find_iter(bytes_before_first_selection)
5199 .map(|result| (0, result)),
5200 );
5201 for (start_offset, query_match) in query_matches {
5202 let query_match = query_match.unwrap(); // can only fail due to I/O
5203 let offset_range =
5204 start_offset + query_match.start()..start_offset + query_match.end();
5205 let display_range = offset_range.start.to_display_point(&display_map)
5206 ..offset_range.end.to_display_point(&display_map);
5207
5208 if !select_next_state.wordwise
5209 || (!movement::is_inside_word(&display_map, display_range.start)
5210 && !movement::is_inside_word(&display_map, display_range.end))
5211 {
5212 next_selected_range = Some(offset_range);
5213 break;
5214 }
5215 }
5216
5217 if let Some(next_selected_range) = next_selected_range {
5218 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5219 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5220 if action.replace_newest {
5221 s.delete(s.newest_anchor().id);
5222 }
5223 s.insert_range(next_selected_range);
5224 });
5225 } else {
5226 select_next_state.done = true;
5227 }
5228 }
5229
5230 self.select_next_state = Some(select_next_state);
5231 } else if selections.len() == 1 {
5232 let selection = selections.last_mut().unwrap();
5233 if selection.start == selection.end {
5234 let word_range = movement::surrounding_word(
5235 &display_map,
5236 selection.start.to_display_point(&display_map),
5237 );
5238 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5239 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5240 selection.goal = SelectionGoal::None;
5241 selection.reversed = false;
5242
5243 let query = buffer
5244 .text_for_range(selection.start..selection.end)
5245 .collect::<String>();
5246 let select_state = SelectNextState {
5247 query: AhoCorasick::new_auto_configured(&[query]),
5248 wordwise: true,
5249 done: false,
5250 };
5251 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5252 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5253 s.select(selections);
5254 });
5255 self.select_next_state = Some(select_state);
5256 } else {
5257 let query = buffer
5258 .text_for_range(selection.start..selection.end)
5259 .collect::<String>();
5260 self.select_next_state = Some(SelectNextState {
5261 query: AhoCorasick::new_auto_configured(&[query]),
5262 wordwise: false,
5263 done: false,
5264 });
5265 self.select_next(action, cx);
5266 }
5267 }
5268 }
5269
5270 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5271 self.push_to_selection_history();
5272 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5273 let buffer = &display_map.buffer_snapshot;
5274 let mut selections = self.selections.all::<usize>(cx);
5275 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5276 let query = &select_prev_state.query;
5277 if !select_prev_state.done {
5278 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5279 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5280 let mut next_selected_range = None;
5281 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5282 let bytes_before_last_selection =
5283 buffer.reversed_bytes_in_range(0..last_selection.start);
5284 let bytes_after_first_selection =
5285 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5286 let query_matches = query
5287 .stream_find_iter(bytes_before_last_selection)
5288 .map(|result| (last_selection.start, result))
5289 .chain(
5290 query
5291 .stream_find_iter(bytes_after_first_selection)
5292 .map(|result| (buffer.len(), result)),
5293 );
5294 for (end_offset, query_match) in query_matches {
5295 let query_match = query_match.unwrap(); // can only fail due to I/O
5296 let offset_range =
5297 end_offset - query_match.end()..end_offset - query_match.start();
5298 let display_range = offset_range.start.to_display_point(&display_map)
5299 ..offset_range.end.to_display_point(&display_map);
5300
5301 if !select_prev_state.wordwise
5302 || (!movement::is_inside_word(&display_map, display_range.start)
5303 && !movement::is_inside_word(&display_map, display_range.end))
5304 {
5305 next_selected_range = Some(offset_range);
5306 break;
5307 }
5308 }
5309
5310 if let Some(next_selected_range) = next_selected_range {
5311 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5312 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5313 if action.replace_newest {
5314 s.delete(s.newest_anchor().id);
5315 }
5316 s.insert_range(next_selected_range);
5317 });
5318 } else {
5319 select_prev_state.done = true;
5320 }
5321 }
5322
5323 self.select_prev_state = Some(select_prev_state);
5324 } else if selections.len() == 1 {
5325 let selection = selections.last_mut().unwrap();
5326 if selection.start == selection.end {
5327 let word_range = movement::surrounding_word(
5328 &display_map,
5329 selection.start.to_display_point(&display_map),
5330 );
5331 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5332 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5333 selection.goal = SelectionGoal::None;
5334 selection.reversed = false;
5335
5336 let query = buffer
5337 .text_for_range(selection.start..selection.end)
5338 .collect::<String>();
5339 let query = query.chars().rev().collect::<String>();
5340 let select_state = SelectNextState {
5341 query: AhoCorasick::new_auto_configured(&[query]),
5342 wordwise: true,
5343 done: false,
5344 };
5345 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5346 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5347 s.select(selections);
5348 });
5349 self.select_prev_state = Some(select_state);
5350 } else {
5351 let query = buffer
5352 .text_for_range(selection.start..selection.end)
5353 .collect::<String>();
5354 let query = query.chars().rev().collect::<String>();
5355 self.select_prev_state = Some(SelectNextState {
5356 query: AhoCorasick::new_auto_configured(&[query]),
5357 wordwise: false,
5358 done: false,
5359 });
5360 self.select_previous(action, cx);
5361 }
5362 }
5363 }
5364
5365 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5366 self.transact(cx, |this, cx| {
5367 let mut selections = this.selections.all::<Point>(cx);
5368 let mut edits = Vec::new();
5369 let mut selection_edit_ranges = Vec::new();
5370 let mut last_toggled_row = None;
5371 let snapshot = this.buffer.read(cx).read(cx);
5372 let empty_str: Arc<str> = "".into();
5373 let mut suffixes_inserted = Vec::new();
5374
5375 fn comment_prefix_range(
5376 snapshot: &MultiBufferSnapshot,
5377 row: u32,
5378 comment_prefix: &str,
5379 comment_prefix_whitespace: &str,
5380 ) -> Range<Point> {
5381 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5382
5383 let mut line_bytes = snapshot
5384 .bytes_in_range(start..snapshot.max_point())
5385 .flatten()
5386 .copied();
5387
5388 // If this line currently begins with the line comment prefix, then record
5389 // the range containing the prefix.
5390 if line_bytes
5391 .by_ref()
5392 .take(comment_prefix.len())
5393 .eq(comment_prefix.bytes())
5394 {
5395 // Include any whitespace that matches the comment prefix.
5396 let matching_whitespace_len = line_bytes
5397 .zip(comment_prefix_whitespace.bytes())
5398 .take_while(|(a, b)| a == b)
5399 .count() as u32;
5400 let end = Point::new(
5401 start.row,
5402 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5403 );
5404 start..end
5405 } else {
5406 start..start
5407 }
5408 }
5409
5410 fn comment_suffix_range(
5411 snapshot: &MultiBufferSnapshot,
5412 row: u32,
5413 comment_suffix: &str,
5414 comment_suffix_has_leading_space: bool,
5415 ) -> Range<Point> {
5416 let end = Point::new(row, snapshot.line_len(row));
5417 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5418
5419 let mut line_end_bytes = snapshot
5420 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5421 .flatten()
5422 .copied();
5423
5424 let leading_space_len = if suffix_start_column > 0
5425 && line_end_bytes.next() == Some(b' ')
5426 && comment_suffix_has_leading_space
5427 {
5428 1
5429 } else {
5430 0
5431 };
5432
5433 // If this line currently begins with the line comment prefix, then record
5434 // the range containing the prefix.
5435 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5436 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5437 start..end
5438 } else {
5439 end..end
5440 }
5441 }
5442
5443 // TODO: Handle selections that cross excerpts
5444 for selection in &mut selections {
5445 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5446 let language = if let Some(language) =
5447 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5448 {
5449 language
5450 } else {
5451 continue;
5452 };
5453
5454 selection_edit_ranges.clear();
5455
5456 // If multiple selections contain a given row, avoid processing that
5457 // row more than once.
5458 let mut start_row = selection.start.row;
5459 if last_toggled_row == Some(start_row) {
5460 start_row += 1;
5461 }
5462 let end_row =
5463 if selection.end.row > selection.start.row && selection.end.column == 0 {
5464 selection.end.row - 1
5465 } else {
5466 selection.end.row
5467 };
5468 last_toggled_row = Some(end_row);
5469
5470 if start_row > end_row {
5471 continue;
5472 }
5473
5474 // If the language has line comments, toggle those.
5475 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5476 // Split the comment prefix's trailing whitespace into a separate string,
5477 // as that portion won't be used for detecting if a line is a comment.
5478 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5479 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5480 let mut all_selection_lines_are_comments = true;
5481
5482 for row in start_row..=end_row {
5483 if snapshot.is_line_blank(row) {
5484 continue;
5485 }
5486
5487 let prefix_range = comment_prefix_range(
5488 snapshot.deref(),
5489 row,
5490 comment_prefix,
5491 comment_prefix_whitespace,
5492 );
5493 if prefix_range.is_empty() {
5494 all_selection_lines_are_comments = false;
5495 }
5496 selection_edit_ranges.push(prefix_range);
5497 }
5498
5499 if all_selection_lines_are_comments {
5500 edits.extend(
5501 selection_edit_ranges
5502 .iter()
5503 .cloned()
5504 .map(|range| (range, empty_str.clone())),
5505 );
5506 } else {
5507 let min_column = selection_edit_ranges
5508 .iter()
5509 .map(|r| r.start.column)
5510 .min()
5511 .unwrap_or(0);
5512 edits.extend(selection_edit_ranges.iter().map(|range| {
5513 let position = Point::new(range.start.row, min_column);
5514 (position..position, full_comment_prefix.clone())
5515 }));
5516 }
5517 } else if let Some((full_comment_prefix, comment_suffix)) =
5518 language.block_comment_delimiters()
5519 {
5520 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5521 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5522 let prefix_range = comment_prefix_range(
5523 snapshot.deref(),
5524 start_row,
5525 comment_prefix,
5526 comment_prefix_whitespace,
5527 );
5528 let suffix_range = comment_suffix_range(
5529 snapshot.deref(),
5530 end_row,
5531 comment_suffix.trim_start_matches(' '),
5532 comment_suffix.starts_with(' '),
5533 );
5534
5535 if prefix_range.is_empty() || suffix_range.is_empty() {
5536 edits.push((
5537 prefix_range.start..prefix_range.start,
5538 full_comment_prefix.clone(),
5539 ));
5540 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5541 suffixes_inserted.push((end_row, comment_suffix.len()));
5542 } else {
5543 edits.push((prefix_range, empty_str.clone()));
5544 edits.push((suffix_range, empty_str.clone()));
5545 }
5546 } else {
5547 continue;
5548 }
5549 }
5550
5551 drop(snapshot);
5552 this.buffer.update(cx, |buffer, cx| {
5553 buffer.edit(edits, None, cx);
5554 });
5555
5556 // Adjust selections so that they end before any comment suffixes that
5557 // were inserted.
5558 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5559 let mut selections = this.selections.all::<Point>(cx);
5560 let snapshot = this.buffer.read(cx).read(cx);
5561 for selection in &mut selections {
5562 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5563 match row.cmp(&selection.end.row) {
5564 Ordering::Less => {
5565 suffixes_inserted.next();
5566 continue;
5567 }
5568 Ordering::Greater => break,
5569 Ordering::Equal => {
5570 if selection.end.column == snapshot.line_len(row) {
5571 if selection.is_empty() {
5572 selection.start.column -= suffix_len as u32;
5573 }
5574 selection.end.column -= suffix_len as u32;
5575 }
5576 break;
5577 }
5578 }
5579 }
5580 }
5581
5582 drop(snapshot);
5583 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5584
5585 let selections = this.selections.all::<Point>(cx);
5586 let selections_on_single_row = selections.windows(2).all(|selections| {
5587 selections[0].start.row == selections[1].start.row
5588 && selections[0].end.row == selections[1].end.row
5589 && selections[0].start.row == selections[0].end.row
5590 });
5591 let selections_selecting = selections
5592 .iter()
5593 .any(|selection| selection.start != selection.end);
5594 let advance_downwards = action.advance_downwards
5595 && selections_on_single_row
5596 && !selections_selecting
5597 && this.mode != EditorMode::SingleLine;
5598
5599 if advance_downwards {
5600 let snapshot = this.buffer.read(cx).snapshot(cx);
5601
5602 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5603 s.move_cursors_with(|display_snapshot, display_point, _| {
5604 let mut point = display_point.to_point(display_snapshot);
5605 point.row += 1;
5606 point = snapshot.clip_point(point, Bias::Left);
5607 let display_point = point.to_display_point(display_snapshot);
5608 (display_point, SelectionGoal::Column(display_point.column()))
5609 })
5610 });
5611 }
5612 });
5613 }
5614
5615 pub fn select_larger_syntax_node(
5616 &mut self,
5617 _: &SelectLargerSyntaxNode,
5618 cx: &mut ViewContext<Self>,
5619 ) {
5620 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5621 let buffer = self.buffer.read(cx).snapshot(cx);
5622 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5623
5624 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5625 let mut selected_larger_node = false;
5626 let new_selections = old_selections
5627 .iter()
5628 .map(|selection| {
5629 let old_range = selection.start..selection.end;
5630 let mut new_range = old_range.clone();
5631 while let Some(containing_range) =
5632 buffer.range_for_syntax_ancestor(new_range.clone())
5633 {
5634 new_range = containing_range;
5635 if !display_map.intersects_fold(new_range.start)
5636 && !display_map.intersects_fold(new_range.end)
5637 {
5638 break;
5639 }
5640 }
5641
5642 selected_larger_node |= new_range != old_range;
5643 Selection {
5644 id: selection.id,
5645 start: new_range.start,
5646 end: new_range.end,
5647 goal: SelectionGoal::None,
5648 reversed: selection.reversed,
5649 }
5650 })
5651 .collect::<Vec<_>>();
5652
5653 if selected_larger_node {
5654 stack.push(old_selections);
5655 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5656 s.select(new_selections);
5657 });
5658 }
5659 self.select_larger_syntax_node_stack = stack;
5660 }
5661
5662 pub fn select_smaller_syntax_node(
5663 &mut self,
5664 _: &SelectSmallerSyntaxNode,
5665 cx: &mut ViewContext<Self>,
5666 ) {
5667 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5668 if let Some(selections) = stack.pop() {
5669 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5670 s.select(selections.to_vec());
5671 });
5672 }
5673 self.select_larger_syntax_node_stack = stack;
5674 }
5675
5676 pub fn move_to_enclosing_bracket(
5677 &mut self,
5678 _: &MoveToEnclosingBracket,
5679 cx: &mut ViewContext<Self>,
5680 ) {
5681 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5682 s.move_offsets_with(|snapshot, selection| {
5683 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5684 return;
5685 };
5686
5687 let mut best_length = usize::MAX;
5688 let mut best_inside = false;
5689 let mut best_in_bracket_range = false;
5690 let mut best_destination = None;
5691 for (open, close) in enclosing_bracket_ranges {
5692 let close = close.to_inclusive();
5693 let length = close.end() - open.start;
5694 let inside = selection.start >= open.end && selection.end <= *close.start();
5695 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5696
5697 // If best is next to a bracket and current isn't, skip
5698 if !in_bracket_range && best_in_bracket_range {
5699 continue;
5700 }
5701
5702 // Prefer smaller lengths unless best is inside and current isn't
5703 if length > best_length && (best_inside || !inside) {
5704 continue;
5705 }
5706
5707 best_length = length;
5708 best_inside = inside;
5709 best_in_bracket_range = in_bracket_range;
5710 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5711 if inside {
5712 open.end
5713 } else {
5714 open.start
5715 }
5716 } else {
5717 if inside {
5718 *close.start()
5719 } else {
5720 *close.end()
5721 }
5722 });
5723 }
5724
5725 if let Some(destination) = best_destination {
5726 selection.collapse_to(destination, SelectionGoal::None);
5727 }
5728 })
5729 });
5730 }
5731
5732 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5733 self.end_selection(cx);
5734 self.selection_history.mode = SelectionHistoryMode::Undoing;
5735 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5736 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5737 self.select_next_state = entry.select_next_state;
5738 self.select_prev_state = entry.select_prev_state;
5739 self.add_selections_state = entry.add_selections_state;
5740 self.request_autoscroll(Autoscroll::newest(), cx);
5741 }
5742 self.selection_history.mode = SelectionHistoryMode::Normal;
5743 }
5744
5745 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5746 self.end_selection(cx);
5747 self.selection_history.mode = SelectionHistoryMode::Redoing;
5748 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5749 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5750 self.select_next_state = entry.select_next_state;
5751 self.select_prev_state = entry.select_prev_state;
5752 self.add_selections_state = entry.add_selections_state;
5753 self.request_autoscroll(Autoscroll::newest(), cx);
5754 }
5755 self.selection_history.mode = SelectionHistoryMode::Normal;
5756 }
5757
5758 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5759 self.go_to_diagnostic_impl(Direction::Next, cx)
5760 }
5761
5762 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
5763 self.go_to_diagnostic_impl(Direction::Prev, cx)
5764 }
5765
5766 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
5767 let buffer = self.buffer.read(cx).snapshot(cx);
5768 let selection = self.selections.newest::<usize>(cx);
5769
5770 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
5771 if direction == Direction::Next {
5772 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
5773 let (group_id, jump_to) = popover.activation_info();
5774 if self.activate_diagnostics(group_id, cx) {
5775 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5776 let mut new_selection = s.newest_anchor().clone();
5777 new_selection.collapse_to(jump_to, SelectionGoal::None);
5778 s.select_anchors(vec![new_selection.clone()]);
5779 });
5780 }
5781 return;
5782 }
5783 }
5784
5785 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
5786 active_diagnostics
5787 .primary_range
5788 .to_offset(&buffer)
5789 .to_inclusive()
5790 });
5791 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
5792 if active_primary_range.contains(&selection.head()) {
5793 *active_primary_range.end()
5794 } else {
5795 selection.head()
5796 }
5797 } else {
5798 selection.head()
5799 };
5800
5801 loop {
5802 let mut diagnostics = if direction == Direction::Prev {
5803 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
5804 } else {
5805 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
5806 };
5807 let group = diagnostics.find_map(|entry| {
5808 if entry.diagnostic.is_primary
5809 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
5810 && !entry.range.is_empty()
5811 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
5812 {
5813 Some((entry.range, entry.diagnostic.group_id))
5814 } else {
5815 None
5816 }
5817 });
5818
5819 if let Some((primary_range, group_id)) = group {
5820 if self.activate_diagnostics(group_id, cx) {
5821 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5822 s.select(vec![Selection {
5823 id: selection.id,
5824 start: primary_range.start,
5825 end: primary_range.start,
5826 reversed: false,
5827 goal: SelectionGoal::None,
5828 }]);
5829 });
5830 }
5831 break;
5832 } else {
5833 // Cycle around to the start of the buffer, potentially moving back to the start of
5834 // the currently active diagnostic.
5835 active_primary_range.take();
5836 if direction == Direction::Prev {
5837 if search_start == buffer.len() {
5838 break;
5839 } else {
5840 search_start = buffer.len();
5841 }
5842 } else if search_start == 0 {
5843 break;
5844 } else {
5845 search_start = 0;
5846 }
5847 }
5848 }
5849 }
5850
5851 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
5852 let snapshot = self
5853 .display_map
5854 .update(cx, |display_map, cx| display_map.snapshot(cx));
5855 let selection = self.selections.newest::<Point>(cx);
5856
5857 if !self.seek_in_direction(
5858 &snapshot,
5859 selection.head(),
5860 false,
5861 snapshot
5862 .buffer_snapshot
5863 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
5864 cx,
5865 ) {
5866 let wrapped_point = Point::zero();
5867 self.seek_in_direction(
5868 &snapshot,
5869 wrapped_point,
5870 true,
5871 snapshot
5872 .buffer_snapshot
5873 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
5874 cx,
5875 );
5876 }
5877 }
5878
5879 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
5880 let snapshot = self
5881 .display_map
5882 .update(cx, |display_map, cx| display_map.snapshot(cx));
5883 let selection = self.selections.newest::<Point>(cx);
5884
5885 if !self.seek_in_direction(
5886 &snapshot,
5887 selection.head(),
5888 false,
5889 snapshot
5890 .buffer_snapshot
5891 .git_diff_hunks_in_range_rev(0..selection.head().row),
5892 cx,
5893 ) {
5894 let wrapped_point = snapshot.buffer_snapshot.max_point();
5895 self.seek_in_direction(
5896 &snapshot,
5897 wrapped_point,
5898 true,
5899 snapshot
5900 .buffer_snapshot
5901 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
5902 cx,
5903 );
5904 }
5905 }
5906
5907 fn seek_in_direction(
5908 &mut self,
5909 snapshot: &DisplaySnapshot,
5910 initial_point: Point,
5911 is_wrapped: bool,
5912 hunks: impl Iterator<Item = DiffHunk<u32>>,
5913 cx: &mut ViewContext<Editor>,
5914 ) -> bool {
5915 let display_point = initial_point.to_display_point(snapshot);
5916 let mut hunks = hunks
5917 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
5918 .skip_while(|hunk| {
5919 if is_wrapped {
5920 false
5921 } else {
5922 hunk.contains_display_row(display_point.row())
5923 }
5924 })
5925 .dedup();
5926
5927 if let Some(hunk) = hunks.next() {
5928 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5929 let row = hunk.start_display_row();
5930 let point = DisplayPoint::new(row, 0);
5931 s.select_display_ranges([point..point]);
5932 });
5933
5934 true
5935 } else {
5936 false
5937 }
5938 }
5939
5940 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
5941 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
5942 }
5943
5944 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
5945 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
5946 }
5947
5948 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
5949 let Some(workspace) = self.workspace(cx) else { return };
5950 let buffer = self.buffer.read(cx);
5951 let head = self.selections.newest::<usize>(cx).head();
5952 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5953 text_anchor
5954 } else {
5955 return;
5956 };
5957
5958 let project = workspace.read(cx).project().clone();
5959 let definitions = project.update(cx, |project, cx| match kind {
5960 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5961 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5962 });
5963
5964 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
5965 let definitions = definitions.await?;
5966 editor.update(&mut cx, |editor, cx| {
5967 editor.navigate_to_definitions(definitions, cx);
5968 })?;
5969 Ok::<(), anyhow::Error>(())
5970 })
5971 .detach_and_log_err(cx);
5972 }
5973
5974 pub fn navigate_to_definitions(
5975 &mut self,
5976 mut definitions: Vec<LocationLink>,
5977 cx: &mut ViewContext<Editor>,
5978 ) {
5979 let Some(workspace) = self.workspace(cx) else { return };
5980 let pane = workspace.read(cx).active_pane().clone();
5981 // If there is one definition, just open it directly
5982 if definitions.len() == 1 {
5983 let definition = definitions.pop().unwrap();
5984 let range = definition
5985 .target
5986 .range
5987 .to_offset(definition.target.buffer.read(cx));
5988
5989 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
5990 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5991 s.select_ranges([range]);
5992 });
5993 } else {
5994 cx.window_context().defer(move |cx| {
5995 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
5996 workspace.open_project_item(definition.target.buffer.clone(), cx)
5997 });
5998 target_editor.update(cx, |target_editor, cx| {
5999 // When selecting a definition in a different buffer, disable the nav history
6000 // to avoid creating a history entry at the previous cursor location.
6001 pane.update(cx, |pane, _| pane.disable_history());
6002 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6003 s.select_ranges([range]);
6004 });
6005 pane.update(cx, |pane, _| pane.enable_history());
6006 });
6007 });
6008 }
6009 } else if !definitions.is_empty() {
6010 let replica_id = self.replica_id(cx);
6011 cx.window_context().defer(move |cx| {
6012 let title = definitions
6013 .iter()
6014 .find(|definition| definition.origin.is_some())
6015 .and_then(|definition| {
6016 definition.origin.as_ref().map(|origin| {
6017 let buffer = origin.buffer.read(cx);
6018 format!(
6019 "Definitions for {}",
6020 buffer
6021 .text_for_range(origin.range.clone())
6022 .collect::<String>()
6023 )
6024 })
6025 })
6026 .unwrap_or("Definitions".to_owned());
6027 let locations = definitions
6028 .into_iter()
6029 .map(|definition| definition.target)
6030 .collect();
6031 workspace.update(cx, |workspace, cx| {
6032 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
6033 });
6034 });
6035 }
6036 }
6037
6038 pub fn find_all_references(
6039 workspace: &mut Workspace,
6040 _: &FindAllReferences,
6041 cx: &mut ViewContext<Workspace>,
6042 ) -> Option<Task<Result<()>>> {
6043 let active_item = workspace.active_item(cx)?;
6044 let editor_handle = active_item.act_as::<Self>(cx)?;
6045
6046 let editor = editor_handle.read(cx);
6047 let buffer = editor.buffer.read(cx);
6048 let head = editor.selections.newest::<usize>(cx).head();
6049 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6050 let replica_id = editor.replica_id(cx);
6051
6052 let project = workspace.project().clone();
6053 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6054 Some(cx.spawn_labeled(
6055 "Finding All References...",
6056 |workspace, mut cx| async move {
6057 let locations = references.await?;
6058 if locations.is_empty() {
6059 return Ok(());
6060 }
6061
6062 workspace.update(&mut cx, |workspace, cx| {
6063 let title = locations
6064 .first()
6065 .as_ref()
6066 .map(|location| {
6067 let buffer = location.buffer.read(cx);
6068 format!(
6069 "References to `{}`",
6070 buffer
6071 .text_for_range(location.range.clone())
6072 .collect::<String>()
6073 )
6074 })
6075 .unwrap();
6076 Self::open_locations_in_multibuffer(
6077 workspace, locations, replica_id, title, cx,
6078 );
6079 })?;
6080
6081 Ok(())
6082 },
6083 ))
6084 }
6085
6086 /// Opens a multibuffer with the given project locations in it
6087 pub fn open_locations_in_multibuffer(
6088 workspace: &mut Workspace,
6089 mut locations: Vec<Location>,
6090 replica_id: ReplicaId,
6091 title: String,
6092 cx: &mut ViewContext<Workspace>,
6093 ) {
6094 // If there are multiple definitions, open them in a multibuffer
6095 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6096 let mut locations = locations.into_iter().peekable();
6097 let mut ranges_to_highlight = Vec::new();
6098
6099 let excerpt_buffer = cx.add_model(|cx| {
6100 let mut multibuffer = MultiBuffer::new(replica_id);
6101 while let Some(location) = locations.next() {
6102 let buffer = location.buffer.read(cx);
6103 let mut ranges_for_buffer = Vec::new();
6104 let range = location.range.to_offset(buffer);
6105 ranges_for_buffer.push(range.clone());
6106
6107 while let Some(next_location) = locations.peek() {
6108 if next_location.buffer == location.buffer {
6109 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6110 locations.next();
6111 } else {
6112 break;
6113 }
6114 }
6115
6116 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6117 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6118 location.buffer.clone(),
6119 ranges_for_buffer,
6120 1,
6121 cx,
6122 ))
6123 }
6124
6125 multibuffer.with_title(title)
6126 });
6127
6128 let editor = cx.add_view(|cx| {
6129 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6130 });
6131 editor.update(cx, |editor, cx| {
6132 editor.highlight_background::<Self>(
6133 ranges_to_highlight,
6134 |theme| theme.editor.highlighted_line_background,
6135 cx,
6136 );
6137 });
6138 workspace.add_item(Box::new(editor), cx);
6139 }
6140
6141 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6142 use language::ToOffset as _;
6143
6144 let project = self.project.clone()?;
6145 let selection = self.selections.newest_anchor().clone();
6146 let (cursor_buffer, cursor_buffer_position) = self
6147 .buffer
6148 .read(cx)
6149 .text_anchor_for_position(selection.head(), cx)?;
6150 let (tail_buffer, _) = self
6151 .buffer
6152 .read(cx)
6153 .text_anchor_for_position(selection.tail(), cx)?;
6154 if tail_buffer != cursor_buffer {
6155 return None;
6156 }
6157
6158 let snapshot = cursor_buffer.read(cx).snapshot();
6159 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6160 let prepare_rename = project.update(cx, |project, cx| {
6161 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6162 });
6163
6164 Some(cx.spawn(|this, mut cx| async move {
6165 let rename_range = if let Some(range) = prepare_rename.await? {
6166 Some(range)
6167 } else {
6168 this.read_with(&cx, |this, cx| {
6169 let buffer = this.buffer.read(cx).snapshot(cx);
6170 let mut buffer_highlights = this
6171 .document_highlights_for_position(selection.head(), &buffer)
6172 .filter(|highlight| {
6173 highlight.start.excerpt_id() == selection.head().excerpt_id()
6174 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6175 });
6176 buffer_highlights
6177 .next()
6178 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6179 })?
6180 };
6181 if let Some(rename_range) = rename_range {
6182 let rename_buffer_range = rename_range.to_offset(&snapshot);
6183 let cursor_offset_in_rename_range =
6184 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6185
6186 this.update(&mut cx, |this, cx| {
6187 this.take_rename(false, cx);
6188 let style = this.style(cx);
6189 let buffer = this.buffer.read(cx).read(cx);
6190 let cursor_offset = selection.head().to_offset(&buffer);
6191 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6192 let rename_end = rename_start + rename_buffer_range.len();
6193 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6194 let mut old_highlight_id = None;
6195 let old_name: Arc<str> = buffer
6196 .chunks(rename_start..rename_end, true)
6197 .map(|chunk| {
6198 if old_highlight_id.is_none() {
6199 old_highlight_id = chunk.syntax_highlight_id;
6200 }
6201 chunk.text
6202 })
6203 .collect::<String>()
6204 .into();
6205
6206 drop(buffer);
6207
6208 // Position the selection in the rename editor so that it matches the current selection.
6209 this.show_local_selections = false;
6210 let rename_editor = cx.add_view(|cx| {
6211 let mut editor = Editor::single_line(None, cx);
6212 if let Some(old_highlight_id) = old_highlight_id {
6213 editor.override_text_style =
6214 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6215 }
6216 editor.buffer.update(cx, |buffer, cx| {
6217 buffer.edit([(0..0, old_name.clone())], None, cx)
6218 });
6219 editor.select_all(&SelectAll, cx);
6220 editor
6221 });
6222
6223 let ranges = this
6224 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6225 .into_iter()
6226 .flat_map(|(_, ranges)| ranges)
6227 .chain(
6228 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6229 .into_iter()
6230 .flat_map(|(_, ranges)| ranges),
6231 )
6232 .collect();
6233
6234 this.highlight_text::<Rename>(
6235 ranges,
6236 HighlightStyle {
6237 fade_out: Some(style.rename_fade),
6238 ..Default::default()
6239 },
6240 cx,
6241 );
6242 cx.focus(&rename_editor);
6243 let block_id = this.insert_blocks(
6244 [BlockProperties {
6245 style: BlockStyle::Flex,
6246 position: range.start.clone(),
6247 height: 1,
6248 render: Arc::new({
6249 let editor = rename_editor.clone();
6250 move |cx: &mut BlockContext| {
6251 ChildView::new(&editor, cx)
6252 .contained()
6253 .with_padding_left(cx.anchor_x)
6254 .into_any()
6255 }
6256 }),
6257 disposition: BlockDisposition::Below,
6258 }],
6259 cx,
6260 )[0];
6261 this.pending_rename = Some(RenameState {
6262 range,
6263 old_name,
6264 editor: rename_editor,
6265 block_id,
6266 });
6267 })?;
6268 }
6269
6270 Ok(())
6271 }))
6272 }
6273
6274 pub fn confirm_rename(
6275 workspace: &mut Workspace,
6276 _: &ConfirmRename,
6277 cx: &mut ViewContext<Workspace>,
6278 ) -> Option<Task<Result<()>>> {
6279 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6280
6281 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6282 let rename = editor.take_rename(false, cx)?;
6283 let buffer = editor.buffer.read(cx);
6284 let (start_buffer, start) =
6285 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6286 let (end_buffer, end) =
6287 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6288 if start_buffer == end_buffer {
6289 let new_name = rename.editor.read(cx).text(cx);
6290 Some((start_buffer, start..end, rename.old_name, new_name))
6291 } else {
6292 None
6293 }
6294 })?;
6295
6296 let rename = workspace.project().clone().update(cx, |project, cx| {
6297 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6298 });
6299
6300 let editor = editor.downgrade();
6301 Some(cx.spawn(|workspace, mut cx| async move {
6302 let project_transaction = rename.await?;
6303 Self::open_project_transaction(
6304 &editor,
6305 workspace,
6306 project_transaction,
6307 format!("Rename: {} → {}", old_name, new_name),
6308 cx.clone(),
6309 )
6310 .await?;
6311
6312 editor.update(&mut cx, |editor, cx| {
6313 editor.refresh_document_highlights(cx);
6314 })?;
6315 Ok(())
6316 }))
6317 }
6318
6319 fn take_rename(
6320 &mut self,
6321 moving_cursor: bool,
6322 cx: &mut ViewContext<Self>,
6323 ) -> Option<RenameState> {
6324 let rename = self.pending_rename.take()?;
6325 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
6326 self.clear_text_highlights::<Rename>(cx);
6327 self.show_local_selections = true;
6328
6329 if moving_cursor {
6330 let rename_editor = rename.editor.read(cx);
6331 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6332
6333 // Update the selection to match the position of the selection inside
6334 // the rename editor.
6335 let snapshot = self.buffer.read(cx).read(cx);
6336 let rename_range = rename.range.to_offset(&snapshot);
6337 let cursor_in_editor = snapshot
6338 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6339 .min(rename_range.end);
6340 drop(snapshot);
6341
6342 self.change_selections(None, cx, |s| {
6343 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6344 });
6345 } else {
6346 self.refresh_document_highlights(cx);
6347 }
6348
6349 Some(rename)
6350 }
6351
6352 #[cfg(any(test, feature = "test-support"))]
6353 pub fn pending_rename(&self) -> Option<&RenameState> {
6354 self.pending_rename.as_ref()
6355 }
6356
6357 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6358 let project = match &self.project {
6359 Some(project) => project.clone(),
6360 None => return None,
6361 };
6362
6363 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6364 }
6365
6366 fn perform_format(
6367 &mut self,
6368 project: ModelHandle<Project>,
6369 trigger: FormatTrigger,
6370 cx: &mut ViewContext<Self>,
6371 ) -> Task<Result<()>> {
6372 let buffer = self.buffer().clone();
6373 let buffers = buffer.read(cx).all_buffers();
6374
6375 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6376 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6377
6378 cx.spawn(|_, mut cx| async move {
6379 let transaction = futures::select_biased! {
6380 _ = timeout => {
6381 log::warn!("timed out waiting for formatting");
6382 None
6383 }
6384 transaction = format.log_err().fuse() => transaction,
6385 };
6386
6387 buffer.update(&mut cx, |buffer, cx| {
6388 if let Some(transaction) = transaction {
6389 if !buffer.is_singleton() {
6390 buffer.push_transaction(&transaction.0, cx);
6391 }
6392 }
6393
6394 cx.notify();
6395 });
6396
6397 Ok(())
6398 })
6399 }
6400
6401 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6402 if let Some(project) = self.project.clone() {
6403 self.buffer.update(cx, |multi_buffer, cx| {
6404 project.update(cx, |project, cx| {
6405 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6406 });
6407 })
6408 }
6409 }
6410
6411 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6412 cx.show_character_palette();
6413 }
6414
6415 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6416 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6417 let buffer = self.buffer.read(cx).snapshot(cx);
6418 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6419 let is_valid = buffer
6420 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6421 .any(|entry| {
6422 entry.diagnostic.is_primary
6423 && !entry.range.is_empty()
6424 && entry.range.start == primary_range_start
6425 && entry.diagnostic.message == active_diagnostics.primary_message
6426 });
6427
6428 if is_valid != active_diagnostics.is_valid {
6429 active_diagnostics.is_valid = is_valid;
6430 let mut new_styles = HashMap::default();
6431 for (block_id, diagnostic) in &active_diagnostics.blocks {
6432 new_styles.insert(
6433 *block_id,
6434 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6435 );
6436 }
6437 self.display_map
6438 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6439 }
6440 }
6441 }
6442
6443 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6444 self.dismiss_diagnostics(cx);
6445 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6446 let buffer = self.buffer.read(cx).snapshot(cx);
6447
6448 let mut primary_range = None;
6449 let mut primary_message = None;
6450 let mut group_end = Point::zero();
6451 let diagnostic_group = buffer
6452 .diagnostic_group::<Point>(group_id)
6453 .map(|entry| {
6454 if entry.range.end > group_end {
6455 group_end = entry.range.end;
6456 }
6457 if entry.diagnostic.is_primary {
6458 primary_range = Some(entry.range.clone());
6459 primary_message = Some(entry.diagnostic.message.clone());
6460 }
6461 entry
6462 })
6463 .collect::<Vec<_>>();
6464 let primary_range = primary_range?;
6465 let primary_message = primary_message?;
6466 let primary_range =
6467 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6468
6469 let blocks = display_map
6470 .insert_blocks(
6471 diagnostic_group.iter().map(|entry| {
6472 let diagnostic = entry.diagnostic.clone();
6473 let message_height = diagnostic.message.lines().count() as u8;
6474 BlockProperties {
6475 style: BlockStyle::Fixed,
6476 position: buffer.anchor_after(entry.range.start),
6477 height: message_height,
6478 render: diagnostic_block_renderer(diagnostic, true),
6479 disposition: BlockDisposition::Below,
6480 }
6481 }),
6482 cx,
6483 )
6484 .into_iter()
6485 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6486 .collect();
6487
6488 Some(ActiveDiagnosticGroup {
6489 primary_range,
6490 primary_message,
6491 blocks,
6492 is_valid: true,
6493 })
6494 });
6495 self.active_diagnostics.is_some()
6496 }
6497
6498 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6499 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6500 self.display_map.update(cx, |display_map, cx| {
6501 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6502 });
6503 cx.notify();
6504 }
6505 }
6506
6507 pub fn set_selections_from_remote(
6508 &mut self,
6509 selections: Vec<Selection<Anchor>>,
6510 pending_selection: Option<Selection<Anchor>>,
6511 cx: &mut ViewContext<Self>,
6512 ) {
6513 let old_cursor_position = self.selections.newest_anchor().head();
6514 self.selections.change_with(cx, |s| {
6515 s.select_anchors(selections);
6516 if let Some(pending_selection) = pending_selection {
6517 s.set_pending(pending_selection, SelectMode::Character);
6518 } else {
6519 s.clear_pending();
6520 }
6521 });
6522 self.selections_did_change(false, &old_cursor_position, cx);
6523 }
6524
6525 fn push_to_selection_history(&mut self) {
6526 self.selection_history.push(SelectionHistoryEntry {
6527 selections: self.selections.disjoint_anchors(),
6528 select_next_state: self.select_next_state.clone(),
6529 select_prev_state: self.select_prev_state.clone(),
6530 add_selections_state: self.add_selections_state.clone(),
6531 });
6532 }
6533
6534 pub fn transact(
6535 &mut self,
6536 cx: &mut ViewContext<Self>,
6537 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6538 ) -> Option<TransactionId> {
6539 self.start_transaction_at(Instant::now(), cx);
6540 update(self, cx);
6541 self.end_transaction_at(Instant::now(), cx)
6542 }
6543
6544 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6545 self.end_selection(cx);
6546 if let Some(tx_id) = self
6547 .buffer
6548 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6549 {
6550 self.selection_history
6551 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6552 }
6553 }
6554
6555 fn end_transaction_at(
6556 &mut self,
6557 now: Instant,
6558 cx: &mut ViewContext<Self>,
6559 ) -> Option<TransactionId> {
6560 if let Some(tx_id) = self
6561 .buffer
6562 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6563 {
6564 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6565 *end_selections = Some(self.selections.disjoint_anchors());
6566 } else {
6567 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
6568 }
6569
6570 cx.emit(Event::Edited);
6571 Some(tx_id)
6572 } else {
6573 None
6574 }
6575 }
6576
6577 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6578 let mut fold_ranges = Vec::new();
6579
6580 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6581
6582 let selections = self.selections.all::<Point>(cx);
6583 for selection in selections {
6584 let range = selection.range().sorted();
6585 let buffer_start_row = range.start.row;
6586
6587 for row in (0..=range.end.row).rev() {
6588 let fold_range = display_map.foldable_range(row);
6589
6590 if let Some(fold_range) = fold_range {
6591 if fold_range.end.row >= buffer_start_row {
6592 fold_ranges.push(fold_range);
6593 if row <= range.start.row {
6594 break;
6595 }
6596 }
6597 }
6598 }
6599 }
6600
6601 self.fold_ranges(fold_ranges, true, cx);
6602 }
6603
6604 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6605 let buffer_row = fold_at.buffer_row;
6606 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6607
6608 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6609 let autoscroll = self
6610 .selections
6611 .all::<Point>(cx)
6612 .iter()
6613 .any(|selection| fold_range.overlaps(&selection.range()));
6614
6615 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6616 }
6617 }
6618
6619 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6620 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6621 let buffer = &display_map.buffer_snapshot;
6622 let selections = self.selections.all::<Point>(cx);
6623 let ranges = selections
6624 .iter()
6625 .map(|s| {
6626 let range = s.display_range(&display_map).sorted();
6627 let mut start = range.start.to_point(&display_map);
6628 let mut end = range.end.to_point(&display_map);
6629 start.column = 0;
6630 end.column = buffer.line_len(end.row);
6631 start..end
6632 })
6633 .collect::<Vec<_>>();
6634
6635 self.unfold_ranges(ranges, true, true, cx);
6636 }
6637
6638 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6639 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6640
6641 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6642 ..Point::new(
6643 unfold_at.buffer_row,
6644 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6645 );
6646
6647 let autoscroll = self
6648 .selections
6649 .all::<Point>(cx)
6650 .iter()
6651 .any(|selection| selection.range().overlaps(&intersection_range));
6652
6653 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6654 }
6655
6656 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6657 let selections = self.selections.all::<Point>(cx);
6658 let ranges = selections.into_iter().map(|s| s.start..s.end);
6659 self.fold_ranges(ranges, true, cx);
6660 }
6661
6662 pub fn fold_ranges<T: ToOffset + Clone>(
6663 &mut self,
6664 ranges: impl IntoIterator<Item = Range<T>>,
6665 auto_scroll: bool,
6666 cx: &mut ViewContext<Self>,
6667 ) {
6668 let mut ranges = ranges.into_iter().peekable();
6669 if ranges.peek().is_some() {
6670 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6671
6672 if auto_scroll {
6673 self.request_autoscroll(Autoscroll::fit(), cx);
6674 }
6675
6676 cx.notify();
6677 }
6678 }
6679
6680 pub fn unfold_ranges<T: ToOffset + Clone>(
6681 &mut self,
6682 ranges: impl IntoIterator<Item = Range<T>>,
6683 inclusive: bool,
6684 auto_scroll: bool,
6685 cx: &mut ViewContext<Self>,
6686 ) {
6687 let mut ranges = ranges.into_iter().peekable();
6688 if ranges.peek().is_some() {
6689 self.display_map
6690 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6691 if auto_scroll {
6692 self.request_autoscroll(Autoscroll::fit(), cx);
6693 }
6694
6695 cx.notify();
6696 }
6697 }
6698
6699 pub fn gutter_hover(
6700 &mut self,
6701 GutterHover { hovered }: &GutterHover,
6702 cx: &mut ViewContext<Self>,
6703 ) {
6704 self.gutter_hovered = *hovered;
6705 cx.notify();
6706 }
6707
6708 pub fn insert_blocks(
6709 &mut self,
6710 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6711 cx: &mut ViewContext<Self>,
6712 ) -> Vec<BlockId> {
6713 let blocks = self
6714 .display_map
6715 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6716 self.request_autoscroll(Autoscroll::fit(), cx);
6717 blocks
6718 }
6719
6720 pub fn replace_blocks(
6721 &mut self,
6722 blocks: HashMap<BlockId, RenderBlock>,
6723 cx: &mut ViewContext<Self>,
6724 ) {
6725 self.display_map
6726 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6727 self.request_autoscroll(Autoscroll::fit(), cx);
6728 }
6729
6730 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
6731 self.display_map.update(cx, |display_map, cx| {
6732 display_map.remove_blocks(block_ids, cx)
6733 });
6734 }
6735
6736 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6737 self.display_map
6738 .update(cx, |map, cx| map.snapshot(cx))
6739 .longest_row()
6740 }
6741
6742 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
6743 self.display_map
6744 .update(cx, |map, cx| map.snapshot(cx))
6745 .max_point()
6746 }
6747
6748 pub fn text(&self, cx: &AppContext) -> String {
6749 self.buffer.read(cx).read(cx).text()
6750 }
6751
6752 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
6753 self.transact(cx, |this, cx| {
6754 this.buffer
6755 .read(cx)
6756 .as_singleton()
6757 .expect("you can only call set_text on editors for singleton buffers")
6758 .update(cx, |buffer, cx| buffer.set_text(text, cx));
6759 });
6760 }
6761
6762 pub fn display_text(&self, cx: &mut AppContext) -> String {
6763 self.display_map
6764 .update(cx, |map, cx| map.snapshot(cx))
6765 .text()
6766 }
6767
6768 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
6769 let settings = self.buffer.read(cx).settings_at(0, cx);
6770 let mode = self
6771 .soft_wrap_mode_override
6772 .unwrap_or_else(|| settings.soft_wrap);
6773 match mode {
6774 language_settings::SoftWrap::None => SoftWrap::None,
6775 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
6776 language_settings::SoftWrap::PreferredLineLength => {
6777 SoftWrap::Column(settings.preferred_line_length)
6778 }
6779 }
6780 }
6781
6782 pub fn set_soft_wrap_mode(
6783 &mut self,
6784 mode: language_settings::SoftWrap,
6785 cx: &mut ViewContext<Self>,
6786 ) {
6787 self.soft_wrap_mode_override = Some(mode);
6788 cx.notify();
6789 }
6790
6791 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
6792 self.display_map
6793 .update(cx, |map, cx| map.set_wrap_width(width, cx))
6794 }
6795
6796 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
6797 if self.soft_wrap_mode_override.is_some() {
6798 self.soft_wrap_mode_override.take();
6799 } else {
6800 let soft_wrap = match self.soft_wrap_mode(cx) {
6801 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
6802 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
6803 };
6804 self.soft_wrap_mode_override = Some(soft_wrap);
6805 }
6806 cx.notify();
6807 }
6808
6809 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
6810 self.show_gutter = show_gutter;
6811 cx.notify();
6812 }
6813
6814 pub fn set_render_excerpt_header(
6815 &mut self,
6816 render_excerpt_header: impl 'static
6817 + Fn(
6818 &mut Editor,
6819 RenderExcerptHeaderParams,
6820 &mut LayoutContext<Editor>,
6821 ) -> AnyElement<Editor>,
6822 cx: &mut ViewContext<Self>,
6823 ) {
6824 self.render_excerpt_header = Some(Arc::new(render_excerpt_header));
6825 cx.notify();
6826 }
6827
6828 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
6829 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6830 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6831 cx.reveal_path(&file.abs_path(cx));
6832 }
6833 }
6834 }
6835
6836 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
6837 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6838 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6839 if let Some(path) = file.abs_path(cx).to_str() {
6840 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6841 }
6842 }
6843 }
6844 }
6845
6846 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
6847 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
6848 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
6849 if let Some(path) = file.path().to_str() {
6850 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
6851 }
6852 }
6853 }
6854 }
6855
6856 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
6857 self.highlighted_rows = rows;
6858 }
6859
6860 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
6861 self.highlighted_rows.clone()
6862 }
6863
6864 pub fn highlight_background<T: 'static>(
6865 &mut self,
6866 ranges: Vec<Range<Anchor>>,
6867 color_fetcher: fn(&Theme) -> Color,
6868 cx: &mut ViewContext<Self>,
6869 ) {
6870 self.background_highlights
6871 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
6872 cx.notify();
6873 }
6874
6875 #[allow(clippy::type_complexity)]
6876 pub fn clear_background_highlights<T: 'static>(
6877 &mut self,
6878 cx: &mut ViewContext<Self>,
6879 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
6880 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
6881 if highlights.is_some() {
6882 cx.notify();
6883 }
6884 highlights
6885 }
6886
6887 #[cfg(feature = "test-support")]
6888 pub fn all_background_highlights(
6889 &mut self,
6890 cx: &mut ViewContext<Self>,
6891 ) -> Vec<(Range<DisplayPoint>, Color)> {
6892 let snapshot = self.snapshot(cx);
6893 let buffer = &snapshot.buffer_snapshot;
6894 let start = buffer.anchor_before(0);
6895 let end = buffer.anchor_after(buffer.len());
6896 let theme = theme::current(cx);
6897 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
6898 }
6899
6900 fn document_highlights_for_position<'a>(
6901 &'a self,
6902 position: Anchor,
6903 buffer: &'a MultiBufferSnapshot,
6904 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
6905 let read_highlights = self
6906 .background_highlights
6907 .get(&TypeId::of::<DocumentHighlightRead>())
6908 .map(|h| &h.1);
6909 let write_highlights = self
6910 .background_highlights
6911 .get(&TypeId::of::<DocumentHighlightWrite>())
6912 .map(|h| &h.1);
6913 let left_position = position.bias_left(buffer);
6914 let right_position = position.bias_right(buffer);
6915 read_highlights
6916 .into_iter()
6917 .chain(write_highlights)
6918 .flat_map(move |ranges| {
6919 let start_ix = match ranges.binary_search_by(|probe| {
6920 let cmp = probe.end.cmp(&left_position, buffer);
6921 if cmp.is_ge() {
6922 Ordering::Greater
6923 } else {
6924 Ordering::Less
6925 }
6926 }) {
6927 Ok(i) | Err(i) => i,
6928 };
6929
6930 let right_position = right_position.clone();
6931 ranges[start_ix..]
6932 .iter()
6933 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
6934 })
6935 }
6936
6937 pub fn background_highlights_in_range(
6938 &self,
6939 search_range: Range<Anchor>,
6940 display_snapshot: &DisplaySnapshot,
6941 theme: &Theme,
6942 ) -> Vec<(Range<DisplayPoint>, Color)> {
6943 let mut results = Vec::new();
6944 let buffer = &display_snapshot.buffer_snapshot;
6945 for (color_fetcher, ranges) in self.background_highlights.values() {
6946 let color = color_fetcher(theme);
6947 let start_ix = match ranges.binary_search_by(|probe| {
6948 let cmp = probe.end.cmp(&search_range.start, buffer);
6949 if cmp.is_gt() {
6950 Ordering::Greater
6951 } else {
6952 Ordering::Less
6953 }
6954 }) {
6955 Ok(i) | Err(i) => i,
6956 };
6957 for range in &ranges[start_ix..] {
6958 if range.start.cmp(&search_range.end, buffer).is_ge() {
6959 break;
6960 }
6961 let start = range
6962 .start
6963 .to_point(buffer)
6964 .to_display_point(display_snapshot);
6965 let end = range
6966 .end
6967 .to_point(buffer)
6968 .to_display_point(display_snapshot);
6969 results.push((start..end, color))
6970 }
6971 }
6972 results
6973 }
6974
6975 pub fn highlight_text<T: 'static>(
6976 &mut self,
6977 ranges: Vec<Range<Anchor>>,
6978 style: HighlightStyle,
6979 cx: &mut ViewContext<Self>,
6980 ) {
6981 self.display_map.update(cx, |map, _| {
6982 map.highlight_text(TypeId::of::<T>(), ranges, style)
6983 });
6984 cx.notify();
6985 }
6986
6987 pub fn text_highlights<'a, T: 'static>(
6988 &'a self,
6989 cx: &'a AppContext,
6990 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6991 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6992 }
6993
6994 pub fn clear_text_highlights<T: 'static>(
6995 &mut self,
6996 cx: &mut ViewContext<Self>,
6997 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6998 let highlights = self
6999 .display_map
7000 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7001 if highlights.is_some() {
7002 cx.notify();
7003 }
7004 highlights
7005 }
7006
7007 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7008 self.blink_manager.read(cx).visible() && self.focused
7009 }
7010
7011 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7012 cx.notify();
7013 }
7014
7015 fn on_buffer_event(
7016 &mut self,
7017 _: ModelHandle<MultiBuffer>,
7018 event: &multi_buffer::Event,
7019 cx: &mut ViewContext<Self>,
7020 ) {
7021 match event {
7022 multi_buffer::Event::Edited => {
7023 self.refresh_active_diagnostics(cx);
7024 self.refresh_code_actions(cx);
7025 if self.has_active_copilot_suggestion(cx) {
7026 self.update_visible_copilot_suggestion(cx);
7027 }
7028 cx.emit(Event::BufferEdited);
7029 }
7030 multi_buffer::Event::ExcerptsAdded {
7031 buffer,
7032 predecessor,
7033 excerpts,
7034 } => cx.emit(Event::ExcerptsAdded {
7035 buffer: buffer.clone(),
7036 predecessor: *predecessor,
7037 excerpts: excerpts.clone(),
7038 }),
7039 multi_buffer::Event::ExcerptsRemoved { ids } => {
7040 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7041 }
7042 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7043 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7044 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7045 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7046 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7047 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7048 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7049 multi_buffer::Event::DiagnosticsUpdated => {
7050 self.refresh_active_diagnostics(cx);
7051 }
7052 _ => {}
7053 }
7054 }
7055
7056 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7057 cx.notify();
7058 }
7059
7060 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7061 self.refresh_copilot_suggestions(true, cx);
7062 }
7063
7064 pub fn set_searchable(&mut self, searchable: bool) {
7065 self.searchable = searchable;
7066 }
7067
7068 pub fn searchable(&self) -> bool {
7069 self.searchable
7070 }
7071
7072 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7073 let active_item = workspace.active_item(cx);
7074 let editor_handle = if let Some(editor) = active_item
7075 .as_ref()
7076 .and_then(|item| item.act_as::<Self>(cx))
7077 {
7078 editor
7079 } else {
7080 cx.propagate_action();
7081 return;
7082 };
7083
7084 let editor = editor_handle.read(cx);
7085 let buffer = editor.buffer.read(cx);
7086 if buffer.is_singleton() {
7087 cx.propagate_action();
7088 return;
7089 }
7090
7091 let mut new_selections_by_buffer = HashMap::default();
7092 for selection in editor.selections.all::<usize>(cx) {
7093 for (buffer, mut range) in
7094 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7095 {
7096 if selection.reversed {
7097 mem::swap(&mut range.start, &mut range.end);
7098 }
7099 new_selections_by_buffer
7100 .entry(buffer)
7101 .or_insert(Vec::new())
7102 .push(range)
7103 }
7104 }
7105
7106 editor_handle.update(cx, |editor, cx| {
7107 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7108 });
7109 let pane = workspace.active_pane().clone();
7110 pane.update(cx, |pane, _| pane.disable_history());
7111
7112 // We defer the pane interaction because we ourselves are a workspace item
7113 // and activating a new item causes the pane to call a method on us reentrantly,
7114 // which panics if we're on the stack.
7115 cx.defer(move |workspace, cx| {
7116 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7117 let editor = workspace.open_project_item::<Self>(buffer, cx);
7118 editor.update(cx, |editor, cx| {
7119 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7120 s.select_ranges(ranges);
7121 });
7122 });
7123 }
7124
7125 pane.update(cx, |pane, _| pane.enable_history());
7126 });
7127 }
7128
7129 fn jump(
7130 workspace: &mut Workspace,
7131 path: ProjectPath,
7132 position: Point,
7133 anchor: language::Anchor,
7134 cx: &mut ViewContext<Workspace>,
7135 ) {
7136 let editor = workspace.open_path(path, None, true, cx);
7137 cx.spawn(|_, mut cx| async move {
7138 let editor = editor
7139 .await?
7140 .downcast::<Editor>()
7141 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7142 .downgrade();
7143 editor.update(&mut cx, |editor, cx| {
7144 let buffer = editor
7145 .buffer()
7146 .read(cx)
7147 .as_singleton()
7148 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7149 let buffer = buffer.read(cx);
7150 let cursor = if buffer.can_resolve(&anchor) {
7151 language::ToPoint::to_point(&anchor, buffer)
7152 } else {
7153 buffer.clip_point(position, Bias::Left)
7154 };
7155
7156 let nav_history = editor.nav_history.take();
7157 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7158 s.select_ranges([cursor..cursor]);
7159 });
7160 editor.nav_history = nav_history;
7161
7162 anyhow::Ok(())
7163 })??;
7164
7165 anyhow::Ok(())
7166 })
7167 .detach_and_log_err(cx);
7168 }
7169
7170 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7171 let snapshot = self.buffer.read(cx).read(cx);
7172 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7173 Some(
7174 ranges
7175 .iter()
7176 .map(move |range| {
7177 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7178 })
7179 .collect(),
7180 )
7181 }
7182
7183 fn selection_replacement_ranges(
7184 &self,
7185 range: Range<OffsetUtf16>,
7186 cx: &AppContext,
7187 ) -> Vec<Range<OffsetUtf16>> {
7188 let selections = self.selections.all::<OffsetUtf16>(cx);
7189 let newest_selection = selections
7190 .iter()
7191 .max_by_key(|selection| selection.id)
7192 .unwrap();
7193 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7194 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7195 let snapshot = self.buffer.read(cx).read(cx);
7196 selections
7197 .into_iter()
7198 .map(|mut selection| {
7199 selection.start.0 =
7200 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7201 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7202 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7203 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7204 })
7205 .collect()
7206 }
7207
7208 fn report_copilot_event(
7209 &self,
7210 suggestion_id: Option<String>,
7211 suggestion_accepted: bool,
7212 cx: &AppContext,
7213 ) {
7214 let Some(project) = &self.project else {
7215 return
7216 };
7217
7218 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7219 let file_extension = self
7220 .buffer
7221 .read(cx)
7222 .as_singleton()
7223 .and_then(|b| b.read(cx).file())
7224 .and_then(|file| Path::new(file.file_name(cx)).extension())
7225 .and_then(|e| e.to_str())
7226 .map(|a| a.to_string());
7227
7228 let telemetry = project.read(cx).client().telemetry().clone();
7229 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7230
7231 let event = ClickhouseEvent::Copilot {
7232 suggestion_id,
7233 suggestion_accepted,
7234 file_extension,
7235 };
7236 telemetry.report_clickhouse_event(event, telemetry_settings);
7237 }
7238
7239 fn report_editor_event(
7240 &self,
7241 name: &'static str,
7242 file_extension: Option<String>,
7243 cx: &AppContext,
7244 ) {
7245 let Some(project) = &self.project else {
7246 return
7247 };
7248
7249 // If None, we are in a file without an extension
7250 let file = self
7251 .buffer
7252 .read(cx)
7253 .as_singleton()
7254 .and_then(|b| b.read(cx).file());
7255 let file_extension = file_extension.or(file
7256 .as_ref()
7257 .and_then(|file| Path::new(file.file_name(cx)).extension())
7258 .and_then(|e| e.to_str())
7259 .map(|a| a.to_string()));
7260
7261 let vim_mode = cx
7262 .global::<SettingsStore>()
7263 .untyped_user_settings()
7264 .get("vim_mode")
7265 == Some(&serde_json::Value::Bool(true));
7266 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7267 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7268 let copilot_enabled_for_language = self
7269 .buffer
7270 .read(cx)
7271 .settings_at(0, cx)
7272 .show_copilot_suggestions;
7273
7274 let telemetry = project.read(cx).client().telemetry().clone();
7275 let event = ClickhouseEvent::Editor {
7276 file_extension,
7277 vim_mode,
7278 operation: name,
7279 copilot_enabled,
7280 copilot_enabled_for_language,
7281 };
7282 telemetry.report_clickhouse_event(event, telemetry_settings)
7283 }
7284
7285 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7286 /// with each line being an array of {text, highlight} objects.
7287 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7288 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7289 return;
7290 };
7291
7292 #[derive(Serialize)]
7293 struct Chunk<'a> {
7294 text: String,
7295 highlight: Option<&'a str>,
7296 }
7297
7298 let snapshot = buffer.read(cx).snapshot();
7299 let range = self
7300 .selected_text_range(cx)
7301 .and_then(|selected_range| {
7302 if selected_range.is_empty() {
7303 None
7304 } else {
7305 Some(selected_range)
7306 }
7307 })
7308 .unwrap_or_else(|| 0..snapshot.len());
7309
7310 let chunks = snapshot.chunks(range, true);
7311 let mut lines = Vec::new();
7312 let mut line: VecDeque<Chunk> = VecDeque::new();
7313
7314 let theme = &theme::current(cx).editor.syntax;
7315
7316 for chunk in chunks {
7317 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7318 let mut chunk_lines = chunk.text.split("\n").peekable();
7319 while let Some(text) = chunk_lines.next() {
7320 let mut merged_with_last_token = false;
7321 if let Some(last_token) = line.back_mut() {
7322 if last_token.highlight == highlight {
7323 last_token.text.push_str(text);
7324 merged_with_last_token = true;
7325 }
7326 }
7327
7328 if !merged_with_last_token {
7329 line.push_back(Chunk {
7330 text: text.into(),
7331 highlight,
7332 });
7333 }
7334
7335 if chunk_lines.peek().is_some() {
7336 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7337 line.pop_front();
7338 }
7339 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7340 line.pop_back();
7341 }
7342
7343 lines.push(mem::take(&mut line));
7344 }
7345 }
7346 }
7347
7348 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7349 cx.write_to_clipboard(ClipboardItem::new(lines));
7350 }
7351}
7352
7353fn consume_contiguous_rows(
7354 contiguous_row_selections: &mut Vec<Selection<Point>>,
7355 selection: &Selection<Point>,
7356 display_map: &DisplaySnapshot,
7357 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7358) -> (u32, u32) {
7359 contiguous_row_selections.push(selection.clone());
7360 let start_row = selection.start.row;
7361 let mut end_row = ending_row(selection, display_map);
7362
7363 while let Some(next_selection) = selections.peek() {
7364 if next_selection.start.row <= end_row {
7365 end_row = ending_row(next_selection, display_map);
7366 contiguous_row_selections.push(selections.next().unwrap().clone());
7367 } else {
7368 break;
7369 }
7370 }
7371 (start_row, end_row)
7372}
7373
7374fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7375 if next_selection.end.column > 0 || next_selection.is_empty() {
7376 display_map.next_line_boundary(next_selection.end).0.row + 1
7377 } else {
7378 next_selection.end.row
7379 }
7380}
7381
7382impl EditorSnapshot {
7383 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7384 self.display_snapshot.buffer_snapshot.language_at(position)
7385 }
7386
7387 pub fn is_focused(&self) -> bool {
7388 self.is_focused
7389 }
7390
7391 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7392 self.placeholder_text.as_ref()
7393 }
7394
7395 pub fn scroll_position(&self) -> Vector2F {
7396 self.scroll_anchor.scroll_position(&self.display_snapshot)
7397 }
7398}
7399
7400impl Deref for EditorSnapshot {
7401 type Target = DisplaySnapshot;
7402
7403 fn deref(&self) -> &Self::Target {
7404 &self.display_snapshot
7405 }
7406}
7407
7408#[derive(Clone, Debug, PartialEq, Eq)]
7409pub enum Event {
7410 InputIgnored {
7411 text: Arc<str>,
7412 },
7413 ExcerptsAdded {
7414 buffer: ModelHandle<Buffer>,
7415 predecessor: ExcerptId,
7416 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7417 },
7418 ExcerptsRemoved {
7419 ids: Vec<ExcerptId>,
7420 },
7421 BufferEdited,
7422 Edited,
7423 Reparsed,
7424 Focused,
7425 Blurred,
7426 DirtyChanged,
7427 Saved,
7428 TitleChanged,
7429 DiffBaseChanged,
7430 SelectionsChanged {
7431 local: bool,
7432 },
7433 ScrollPositionChanged {
7434 local: bool,
7435 },
7436 Closed,
7437}
7438
7439pub struct EditorFocused(pub ViewHandle<Editor>);
7440pub struct EditorBlurred(pub ViewHandle<Editor>);
7441pub struct EditorReleased(pub WeakViewHandle<Editor>);
7442
7443impl Entity for Editor {
7444 type Event = Event;
7445
7446 fn release(&mut self, cx: &mut AppContext) {
7447 cx.emit_global(EditorReleased(self.handle.clone()));
7448 }
7449}
7450
7451impl View for Editor {
7452 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7453 let style = self.style(cx);
7454 let font_changed = self.display_map.update(cx, |map, cx| {
7455 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7456 map.set_font(style.text.font_id, style.text.font_size, cx)
7457 });
7458
7459 if font_changed {
7460 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7461 hide_hover(editor, cx);
7462 hide_link_definition(editor, cx);
7463 });
7464 }
7465
7466 let mut editor = EditorElement::new(style.clone());
7467 if let Some(render_excerpt_header) = self.render_excerpt_header.clone() {
7468 editor = editor.with_render_excerpt_header(render_excerpt_header);
7469 }
7470 Stack::new()
7471 .with_child(editor)
7472 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7473 .into_any()
7474 }
7475
7476 fn ui_name() -> &'static str {
7477 "Editor"
7478 }
7479
7480 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7481 if cx.is_self_focused() {
7482 let focused_event = EditorFocused(cx.handle());
7483 cx.emit(Event::Focused);
7484 cx.emit_global(focused_event);
7485 }
7486 if let Some(rename) = self.pending_rename.as_ref() {
7487 cx.focus(&rename.editor);
7488 } else {
7489 if !self.focused {
7490 self.blink_manager.update(cx, BlinkManager::enable);
7491 }
7492 self.focused = true;
7493 self.buffer.update(cx, |buffer, cx| {
7494 buffer.finalize_last_transaction(cx);
7495 if self.leader_replica_id.is_none() {
7496 buffer.set_active_selections(
7497 &self.selections.disjoint_anchors(),
7498 self.selections.line_mode,
7499 self.cursor_shape,
7500 cx,
7501 );
7502 }
7503 });
7504 }
7505 }
7506
7507 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7508 let blurred_event = EditorBlurred(cx.handle());
7509 cx.emit_global(blurred_event);
7510 self.focused = false;
7511 self.blink_manager.update(cx, BlinkManager::disable);
7512 self.buffer
7513 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7514 self.hide_context_menu(cx);
7515 hide_hover(self, cx);
7516 cx.emit(Event::Blurred);
7517 cx.notify();
7518 }
7519
7520 fn modifiers_changed(
7521 &mut self,
7522 event: &gpui::platform::ModifiersChangedEvent,
7523 cx: &mut ViewContext<Self>,
7524 ) -> bool {
7525 let pending_selection = self.has_pending_selection();
7526
7527 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7528 if event.cmd && !pending_selection {
7529 let snapshot = self.snapshot(cx);
7530 let kind = if event.shift {
7531 LinkDefinitionKind::Type
7532 } else {
7533 LinkDefinitionKind::Symbol
7534 };
7535
7536 show_link_definition(kind, self, point, snapshot, cx);
7537 return false;
7538 }
7539 }
7540
7541 {
7542 if self.link_go_to_definition_state.symbol_range.is_some()
7543 || !self.link_go_to_definition_state.definitions.is_empty()
7544 {
7545 self.link_go_to_definition_state.symbol_range.take();
7546 self.link_go_to_definition_state.definitions.clear();
7547 cx.notify();
7548 }
7549
7550 self.link_go_to_definition_state.task = None;
7551
7552 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7553 }
7554
7555 false
7556 }
7557
7558 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7559 Self::reset_to_default_keymap_context(keymap);
7560 let mode = match self.mode {
7561 EditorMode::SingleLine => "single_line",
7562 EditorMode::AutoHeight { .. } => "auto_height",
7563 EditorMode::Full => "full",
7564 };
7565 keymap.add_key("mode", mode);
7566 if self.pending_rename.is_some() {
7567 keymap.add_identifier("renaming");
7568 }
7569 match self.context_menu.as_ref() {
7570 Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
7571 Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
7572 None => {}
7573 }
7574 for layer in self.keymap_context_layers.values() {
7575 keymap.extend(layer);
7576 }
7577
7578 if let Some(extension) = self
7579 .buffer
7580 .read(cx)
7581 .as_singleton()
7582 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7583 {
7584 keymap.add_key("extension", extension.to_string());
7585 }
7586 }
7587
7588 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7589 Some(
7590 self.buffer
7591 .read(cx)
7592 .read(cx)
7593 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7594 .collect(),
7595 )
7596 }
7597
7598 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7599 // Prevent the IME menu from appearing when holding down an alphabetic key
7600 // while input is disabled.
7601 if !self.input_enabled {
7602 return None;
7603 }
7604
7605 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7606 Some(range.start.0..range.end.0)
7607 }
7608
7609 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7610 let snapshot = self.buffer.read(cx).read(cx);
7611 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7612 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7613 }
7614
7615 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7616 self.clear_text_highlights::<InputComposition>(cx);
7617 self.ime_transaction.take();
7618 }
7619
7620 fn replace_text_in_range(
7621 &mut self,
7622 range_utf16: Option<Range<usize>>,
7623 text: &str,
7624 cx: &mut ViewContext<Self>,
7625 ) {
7626 self.transact(cx, |this, cx| {
7627 if this.input_enabled {
7628 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7629 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7630 Some(this.selection_replacement_ranges(range_utf16, cx))
7631 } else {
7632 this.marked_text_ranges(cx)
7633 };
7634
7635 if let Some(new_selected_ranges) = new_selected_ranges {
7636 this.change_selections(None, cx, |selections| {
7637 selections.select_ranges(new_selected_ranges)
7638 });
7639 }
7640 }
7641
7642 this.handle_input(text, cx);
7643 });
7644
7645 if !self.input_enabled {
7646 return;
7647 }
7648
7649 if let Some(transaction) = self.ime_transaction {
7650 self.buffer.update(cx, |buffer, cx| {
7651 buffer.group_until_transaction(transaction, cx);
7652 });
7653 }
7654
7655 self.unmark_text(cx);
7656 }
7657
7658 fn replace_and_mark_text_in_range(
7659 &mut self,
7660 range_utf16: Option<Range<usize>>,
7661 text: &str,
7662 new_selected_range_utf16: Option<Range<usize>>,
7663 cx: &mut ViewContext<Self>,
7664 ) {
7665 if !self.input_enabled {
7666 return;
7667 }
7668
7669 let transaction = self.transact(cx, |this, cx| {
7670 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7671 let snapshot = this.buffer.read(cx).read(cx);
7672 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7673 for marked_range in &mut marked_ranges {
7674 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7675 marked_range.start.0 += relative_range_utf16.start;
7676 marked_range.start =
7677 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7678 marked_range.end =
7679 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7680 }
7681 }
7682 Some(marked_ranges)
7683 } else if let Some(range_utf16) = range_utf16 {
7684 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7685 Some(this.selection_replacement_ranges(range_utf16, cx))
7686 } else {
7687 None
7688 };
7689
7690 if let Some(ranges) = ranges_to_replace {
7691 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7692 }
7693
7694 let marked_ranges = {
7695 let snapshot = this.buffer.read(cx).read(cx);
7696 this.selections
7697 .disjoint_anchors()
7698 .iter()
7699 .map(|selection| {
7700 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7701 })
7702 .collect::<Vec<_>>()
7703 };
7704
7705 if text.is_empty() {
7706 this.unmark_text(cx);
7707 } else {
7708 this.highlight_text::<InputComposition>(
7709 marked_ranges.clone(),
7710 this.style(cx).composition_mark,
7711 cx,
7712 );
7713 }
7714
7715 this.handle_input(text, cx);
7716
7717 if let Some(new_selected_range) = new_selected_range_utf16 {
7718 let snapshot = this.buffer.read(cx).read(cx);
7719 let new_selected_ranges = marked_ranges
7720 .into_iter()
7721 .map(|marked_range| {
7722 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7723 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7724 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7725 snapshot.clip_offset_utf16(new_start, Bias::Left)
7726 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7727 })
7728 .collect::<Vec<_>>();
7729
7730 drop(snapshot);
7731 this.change_selections(None, cx, |selections| {
7732 selections.select_ranges(new_selected_ranges)
7733 });
7734 }
7735 });
7736
7737 self.ime_transaction = self.ime_transaction.or(transaction);
7738 if let Some(transaction) = self.ime_transaction {
7739 self.buffer.update(cx, |buffer, cx| {
7740 buffer.group_until_transaction(transaction, cx);
7741 });
7742 }
7743
7744 if self.text_highlights::<InputComposition>(cx).is_none() {
7745 self.ime_transaction.take();
7746 }
7747 }
7748}
7749
7750fn build_style(
7751 settings: &ThemeSettings,
7752 get_field_editor_theme: Option<&GetFieldEditorTheme>,
7753 override_text_style: Option<&OverrideTextStyle>,
7754 cx: &AppContext,
7755) -> EditorStyle {
7756 let font_cache = cx.font_cache();
7757
7758 let theme_id = settings.theme.meta.id;
7759 let mut theme = settings.theme.editor.clone();
7760 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
7761 let field_editor_theme = get_field_editor_theme(&settings.theme);
7762 theme.text_color = field_editor_theme.text.color;
7763 theme.selection = field_editor_theme.selection;
7764 theme.background = field_editor_theme
7765 .container
7766 .background_color
7767 .unwrap_or_default();
7768 EditorStyle {
7769 text: field_editor_theme.text,
7770 placeholder_text: field_editor_theme.placeholder_text,
7771 theme,
7772 theme_id,
7773 }
7774 } else {
7775 let font_family_id = settings.buffer_font_family;
7776 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
7777 let font_properties = Default::default();
7778 let font_id = font_cache
7779 .select_font(font_family_id, &font_properties)
7780 .unwrap();
7781 let font_size = settings.buffer_font_size(cx);
7782 EditorStyle {
7783 text: TextStyle {
7784 color: settings.theme.editor.text_color,
7785 font_family_name,
7786 font_family_id,
7787 font_id,
7788 font_size,
7789 font_properties,
7790 underline: Default::default(),
7791 },
7792 placeholder_text: None,
7793 theme,
7794 theme_id,
7795 }
7796 };
7797
7798 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
7799 if let Some(highlighted) = style
7800 .text
7801 .clone()
7802 .highlight(highlight_style, font_cache)
7803 .log_err()
7804 {
7805 style.text = highlighted;
7806 }
7807 }
7808
7809 style
7810}
7811
7812trait SelectionExt {
7813 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
7814 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
7815 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
7816 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
7817 -> Range<u32>;
7818}
7819
7820impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
7821 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
7822 let start = self.start.to_point(buffer);
7823 let end = self.end.to_point(buffer);
7824 if self.reversed {
7825 end..start
7826 } else {
7827 start..end
7828 }
7829 }
7830
7831 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
7832 let start = self.start.to_offset(buffer);
7833 let end = self.end.to_offset(buffer);
7834 if self.reversed {
7835 end..start
7836 } else {
7837 start..end
7838 }
7839 }
7840
7841 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
7842 let start = self
7843 .start
7844 .to_point(&map.buffer_snapshot)
7845 .to_display_point(map);
7846 let end = self
7847 .end
7848 .to_point(&map.buffer_snapshot)
7849 .to_display_point(map);
7850 if self.reversed {
7851 end..start
7852 } else {
7853 start..end
7854 }
7855 }
7856
7857 fn spanned_rows(
7858 &self,
7859 include_end_if_at_line_start: bool,
7860 map: &DisplaySnapshot,
7861 ) -> Range<u32> {
7862 let start = self.start.to_point(&map.buffer_snapshot);
7863 let mut end = self.end.to_point(&map.buffer_snapshot);
7864 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
7865 end.row -= 1;
7866 }
7867
7868 let buffer_start = map.prev_line_boundary(start).0;
7869 let buffer_end = map.next_line_boundary(end).0;
7870 buffer_start.row..buffer_end.row + 1
7871 }
7872}
7873
7874impl<T: InvalidationRegion> InvalidationStack<T> {
7875 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
7876 where
7877 S: Clone + ToOffset,
7878 {
7879 while let Some(region) = self.last() {
7880 let all_selections_inside_invalidation_ranges =
7881 if selections.len() == region.ranges().len() {
7882 selections
7883 .iter()
7884 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
7885 .all(|(selection, invalidation_range)| {
7886 let head = selection.head().to_offset(buffer);
7887 invalidation_range.start <= head && invalidation_range.end >= head
7888 })
7889 } else {
7890 false
7891 };
7892
7893 if all_selections_inside_invalidation_ranges {
7894 break;
7895 } else {
7896 self.pop();
7897 }
7898 }
7899 }
7900}
7901
7902impl<T> Default for InvalidationStack<T> {
7903 fn default() -> Self {
7904 Self(Default::default())
7905 }
7906}
7907
7908impl<T> Deref for InvalidationStack<T> {
7909 type Target = Vec<T>;
7910
7911 fn deref(&self) -> &Self::Target {
7912 &self.0
7913 }
7914}
7915
7916impl<T> DerefMut for InvalidationStack<T> {
7917 fn deref_mut(&mut self) -> &mut Self::Target {
7918 &mut self.0
7919 }
7920}
7921
7922impl InvalidationRegion for SnippetState {
7923 fn ranges(&self) -> &[Range<Anchor>] {
7924 &self.ranges[self.active_index]
7925 }
7926}
7927
7928impl Deref for EditorStyle {
7929 type Target = theme::Editor;
7930
7931 fn deref(&self) -> &Self::Target {
7932 &self.theme
7933 }
7934}
7935
7936pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
7937 let mut highlighted_lines = Vec::new();
7938 for (index, line) in diagnostic.message.lines().enumerate() {
7939 let line = match &diagnostic.source {
7940 Some(source) if index == 0 => {
7941 let source_highlight = Vec::from_iter(0..source.len());
7942 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
7943 }
7944
7945 _ => highlight_diagnostic_message(Vec::new(), line),
7946 };
7947 highlighted_lines.push(line);
7948 }
7949
7950 Arc::new(move |cx: &mut BlockContext| {
7951 let settings = settings::get::<ThemeSettings>(cx);
7952 let theme = &settings.theme.editor;
7953 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
7954 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
7955 Flex::column()
7956 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
7957 Label::new(
7958 line.clone(),
7959 style.message.clone().with_font_size(font_size),
7960 )
7961 .with_highlights(highlights.clone())
7962 .contained()
7963 .with_margin_left(cx.anchor_x)
7964 }))
7965 .aligned()
7966 .left()
7967 .into_any()
7968 })
7969}
7970
7971pub fn highlight_diagnostic_message(
7972 initial_highlights: Vec<usize>,
7973 message: &str,
7974) -> (String, Vec<usize>) {
7975 let mut message_without_backticks = String::new();
7976 let mut prev_offset = 0;
7977 let mut inside_block = false;
7978 let mut highlights = initial_highlights;
7979 for (match_ix, (offset, _)) in message
7980 .match_indices('`')
7981 .chain([(message.len(), "")])
7982 .enumerate()
7983 {
7984 message_without_backticks.push_str(&message[prev_offset..offset]);
7985 if inside_block {
7986 highlights.extend(prev_offset - match_ix..offset - match_ix);
7987 }
7988
7989 inside_block = !inside_block;
7990 prev_offset = offset + 1;
7991 }
7992
7993 (message_without_backticks, highlights)
7994}
7995
7996pub fn diagnostic_style(
7997 severity: DiagnosticSeverity,
7998 valid: bool,
7999 theme: &theme::Editor,
8000) -> DiagnosticStyle {
8001 match (severity, valid) {
8002 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8003 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8004 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8005 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8006 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8007 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8008 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8009 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8010 _ => theme.invalid_hint_diagnostic.clone(),
8011 }
8012}
8013
8014pub fn combine_syntax_and_fuzzy_match_highlights(
8015 text: &str,
8016 default_style: HighlightStyle,
8017 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8018 match_indices: &[usize],
8019) -> Vec<(Range<usize>, HighlightStyle)> {
8020 let mut result = Vec::new();
8021 let mut match_indices = match_indices.iter().copied().peekable();
8022
8023 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8024 {
8025 syntax_highlight.weight = None;
8026
8027 // Add highlights for any fuzzy match characters before the next
8028 // syntax highlight range.
8029 while let Some(&match_index) = match_indices.peek() {
8030 if match_index >= range.start {
8031 break;
8032 }
8033 match_indices.next();
8034 let end_index = char_ix_after(match_index, text);
8035 let mut match_style = default_style;
8036 match_style.weight = Some(fonts::Weight::BOLD);
8037 result.push((match_index..end_index, match_style));
8038 }
8039
8040 if range.start == usize::MAX {
8041 break;
8042 }
8043
8044 // Add highlights for any fuzzy match characters within the
8045 // syntax highlight range.
8046 let mut offset = range.start;
8047 while let Some(&match_index) = match_indices.peek() {
8048 if match_index >= range.end {
8049 break;
8050 }
8051
8052 match_indices.next();
8053 if match_index > offset {
8054 result.push((offset..match_index, syntax_highlight));
8055 }
8056
8057 let mut end_index = char_ix_after(match_index, text);
8058 while let Some(&next_match_index) = match_indices.peek() {
8059 if next_match_index == end_index && next_match_index < range.end {
8060 end_index = char_ix_after(next_match_index, text);
8061 match_indices.next();
8062 } else {
8063 break;
8064 }
8065 }
8066
8067 let mut match_style = syntax_highlight;
8068 match_style.weight = Some(fonts::Weight::BOLD);
8069 result.push((match_index..end_index, match_style));
8070 offset = end_index;
8071 }
8072
8073 if offset < range.end {
8074 result.push((offset..range.end, syntax_highlight));
8075 }
8076 }
8077
8078 fn char_ix_after(ix: usize, text: &str) -> usize {
8079 ix + text[ix..].chars().next().unwrap().len_utf8()
8080 }
8081
8082 result
8083}
8084
8085pub fn styled_runs_for_code_label<'a>(
8086 label: &'a CodeLabel,
8087 syntax_theme: &'a theme::SyntaxTheme,
8088) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8089 let fade_out = HighlightStyle {
8090 fade_out: Some(0.35),
8091 ..Default::default()
8092 };
8093
8094 let mut prev_end = label.filter_range.end;
8095 label
8096 .runs
8097 .iter()
8098 .enumerate()
8099 .flat_map(move |(ix, (range, highlight_id))| {
8100 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8101 style
8102 } else {
8103 return Default::default();
8104 };
8105 let mut muted_style = style;
8106 muted_style.highlight(fade_out);
8107
8108 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8109 if range.start >= label.filter_range.end {
8110 if range.start > prev_end {
8111 runs.push((prev_end..range.start, fade_out));
8112 }
8113 runs.push((range.clone(), muted_style));
8114 } else if range.end <= label.filter_range.end {
8115 runs.push((range.clone(), style));
8116 } else {
8117 runs.push((range.start..label.filter_range.end, style));
8118 runs.push((label.filter_range.end..range.end, muted_style));
8119 }
8120 prev_end = cmp::max(prev_end, range.end);
8121
8122 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8123 runs.push((prev_end..label.text.len(), fade_out));
8124 }
8125
8126 runs
8127 })
8128}
8129
8130pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8131 let mut index = 0;
8132 let mut codepoints = text.char_indices().peekable();
8133
8134 std::iter::from_fn(move || {
8135 let start_index = index;
8136 while let Some((new_index, codepoint)) = codepoints.next() {
8137 index = new_index + codepoint.len_utf8();
8138 let current_upper = codepoint.is_uppercase();
8139 let next_upper = codepoints
8140 .peek()
8141 .map(|(_, c)| c.is_uppercase())
8142 .unwrap_or(false);
8143
8144 if !current_upper && next_upper {
8145 return Some(&text[start_index..index]);
8146 }
8147 }
8148
8149 index = text.len();
8150 if start_index < text.len() {
8151 return Some(&text[start_index..]);
8152 }
8153 None
8154 })
8155 .flat_map(|word| word.split_inclusive('_'))
8156}
8157
8158trait RangeToAnchorExt {
8159 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8160}
8161
8162impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8163 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8164 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8165 }
8166}