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