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