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 {
2881 if pair_state.selection_id == selection.id {
2882 enclosing = Some(pair_state);
2883 }
2884 i += 1;
2885 }
2886 }
2887
2888 (selection.clone(), enclosing)
2889 })
2890 }
2891
2892 /// Remove any autoclose regions that no longer contain their selection.
2893 fn invalidate_autoclose_regions(
2894 &mut self,
2895 mut selections: &[Selection<Anchor>],
2896 buffer: &MultiBufferSnapshot,
2897 ) {
2898 self.autoclose_regions.retain(|state| {
2899 let mut i = 0;
2900 while let Some(selection) = selections.get(i) {
2901 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2902 selections = &selections[1..];
2903 continue;
2904 }
2905 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2906 break;
2907 }
2908 if selection.id == state.selection_id {
2909 return true;
2910 } else {
2911 i += 1;
2912 }
2913 }
2914 false
2915 });
2916 }
2917 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2918 let offset = position.to_offset(buffer);
2919 let (word_range, kind) = buffer.surrounding_word(offset);
2920 if offset > word_range.start && kind == Some(CharKind::Word) {
2921 Some(
2922 buffer
2923 .text_for_range(word_range.start..offset)
2924 .collect::<String>(),
2925 )
2926 } else {
2927 None
2928 }
2929 }
2930
2931 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
2932 self.refresh_inlay_hints(
2933 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
2934 cx,
2935 );
2936 }
2937
2938 pub fn inlay_hints_enabled(&self) -> bool {
2939 self.inlay_hint_cache.enabled
2940 }
2941
2942 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
2943 if self.project.is_none() || self.mode != EditorMode::Full {
2944 return;
2945 }
2946
2947 let reason_description = reason.description();
2948 let (invalidate_cache, required_languages) = match reason {
2949 InlayHintRefreshReason::Toggle(enabled) => {
2950 self.inlay_hint_cache.enabled = enabled;
2951 if enabled {
2952 (InvalidationStrategy::RefreshRequested, None)
2953 } else {
2954 self.inlay_hint_cache.clear();
2955 self.splice_inlay_hints(
2956 self.visible_inlay_hints(cx)
2957 .iter()
2958 .map(|inlay| inlay.id)
2959 .collect(),
2960 Vec::new(),
2961 cx,
2962 );
2963 return;
2964 }
2965 }
2966 InlayHintRefreshReason::SettingsChange(new_settings) => {
2967 match self.inlay_hint_cache.update_settings(
2968 &self.buffer,
2969 new_settings,
2970 self.visible_inlay_hints(cx),
2971 cx,
2972 ) {
2973 ControlFlow::Break(Some(InlaySplice {
2974 to_remove,
2975 to_insert,
2976 })) => {
2977 self.splice_inlay_hints(to_remove, to_insert, cx);
2978 return;
2979 }
2980 ControlFlow::Break(None) => return,
2981 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2982 }
2983 }
2984 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
2985 if let Some(InlaySplice {
2986 to_remove,
2987 to_insert,
2988 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
2989 {
2990 self.splice_inlay_hints(to_remove, to_insert, cx);
2991 }
2992 return;
2993 }
2994 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2995 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
2996 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2997 }
2998 InlayHintRefreshReason::RefreshRequested => {
2999 (InvalidationStrategy::RefreshRequested, None)
3000 }
3001 };
3002
3003 if let Some(InlaySplice {
3004 to_remove,
3005 to_insert,
3006 }) = self.inlay_hint_cache.spawn_hint_refresh(
3007 reason_description,
3008 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
3009 invalidate_cache,
3010 cx,
3011 ) {
3012 self.splice_inlay_hints(to_remove, to_insert, cx);
3013 }
3014 }
3015
3016 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
3017 self.display_map
3018 .read(cx)
3019 .current_inlays()
3020 .filter(move |inlay| {
3021 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
3022 })
3023 .cloned()
3024 .collect()
3025 }
3026
3027 pub fn excerpt_visible_offsets(
3028 &self,
3029 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3030 cx: &mut ViewContext<'_, '_, Editor>,
3031 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
3032 let multi_buffer = self.buffer().read(cx);
3033 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3034 let multi_buffer_visible_start = self
3035 .scroll_manager
3036 .anchor()
3037 .anchor
3038 .to_point(&multi_buffer_snapshot);
3039 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3040 multi_buffer_visible_start
3041 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3042 Bias::Left,
3043 );
3044 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3045 multi_buffer
3046 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
3047 .into_iter()
3048 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3049 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
3050 let buffer = buffer_handle.read(cx);
3051 let language = buffer.language()?;
3052 if let Some(restrict_to_languages) = restrict_to_languages {
3053 if !restrict_to_languages.contains(language) {
3054 return None;
3055 }
3056 }
3057 Some((
3058 excerpt_id,
3059 (
3060 buffer_handle,
3061 buffer.version().clone(),
3062 excerpt_visible_range,
3063 ),
3064 ))
3065 })
3066 .collect()
3067 }
3068
3069 fn splice_inlay_hints(
3070 &self,
3071 to_remove: Vec<InlayId>,
3072 to_insert: Vec<Inlay>,
3073 cx: &mut ViewContext<Self>,
3074 ) {
3075 self.display_map.update(cx, |display_map, cx| {
3076 display_map.splice_inlays(to_remove, to_insert, cx);
3077 });
3078 cx.notify();
3079 }
3080
3081 fn trigger_on_type_formatting(
3082 &self,
3083 input: String,
3084 cx: &mut ViewContext<Self>,
3085 ) -> Option<Task<Result<()>>> {
3086 if input.len() != 1 {
3087 return None;
3088 }
3089
3090 let project = self.project.as_ref()?;
3091 let position = self.selections.newest_anchor().head();
3092 let (buffer, buffer_position) = self
3093 .buffer
3094 .read(cx)
3095 .text_anchor_for_position(position.clone(), cx)?;
3096
3097 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3098 // hence we do LSP request & edit on host side only — add formats to host's history.
3099 let push_to_lsp_host_history = true;
3100 // If this is not the host, append its history with new edits.
3101 let push_to_client_history = project.read(cx).is_remote();
3102
3103 let on_type_formatting = project.update(cx, |project, cx| {
3104 project.on_type_format(
3105 buffer.clone(),
3106 buffer_position,
3107 input,
3108 push_to_lsp_host_history,
3109 cx,
3110 )
3111 });
3112 Some(cx.spawn(|editor, mut cx| async move {
3113 if let Some(transaction) = on_type_formatting.await? {
3114 if push_to_client_history {
3115 buffer.update(&mut cx, |buffer, _| {
3116 buffer.push_transaction(transaction, Instant::now());
3117 });
3118 }
3119 editor.update(&mut cx, |editor, cx| {
3120 editor.refresh_document_highlights(cx);
3121 })?;
3122 }
3123 Ok(())
3124 }))
3125 }
3126
3127 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3128 if self.pending_rename.is_some() {
3129 return;
3130 }
3131
3132 let project = if let Some(project) = self.project.clone() {
3133 project
3134 } else {
3135 return;
3136 };
3137
3138 let position = self.selections.newest_anchor().head();
3139 let (buffer, buffer_position) = if let Some(output) = self
3140 .buffer
3141 .read(cx)
3142 .text_anchor_for_position(position.clone(), cx)
3143 {
3144 output
3145 } else {
3146 return;
3147 };
3148
3149 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3150 let completions = project.update(cx, |project, cx| {
3151 project.completions(&buffer, buffer_position, cx)
3152 });
3153
3154 let id = post_inc(&mut self.next_completion_id);
3155 let project = self.project.clone();
3156 let task = cx.spawn(|this, mut cx| {
3157 async move {
3158 let menu = if let Some(completions) = completions.await.log_err() {
3159 let mut menu = CompletionsMenu {
3160 id,
3161 initial_position: position,
3162 match_candidates: completions
3163 .iter()
3164 .enumerate()
3165 .map(|(id, completion)| {
3166 StringMatchCandidate::new(
3167 id,
3168 completion.label.text[completion.label.filter_range.clone()]
3169 .into(),
3170 )
3171 })
3172 .collect(),
3173 buffer,
3174 project,
3175 completions: completions.into(),
3176 matches: Vec::new().into(),
3177 selected_item: 0,
3178 list: Default::default(),
3179 };
3180 menu.filter(query.as_deref(), cx.background()).await;
3181 if menu.matches.is_empty() {
3182 None
3183 } else {
3184 Some(menu)
3185 }
3186 } else {
3187 None
3188 };
3189
3190 this.update(&mut cx, |this, cx| {
3191 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
3192
3193 match this.context_menu.as_ref() {
3194 None => {}
3195 Some(ContextMenu::Completions(prev_menu)) => {
3196 if prev_menu.id > id {
3197 return;
3198 }
3199 }
3200 _ => return,
3201 }
3202
3203 if this.focused && menu.is_some() {
3204 let menu = menu.unwrap();
3205 this.show_context_menu(ContextMenu::Completions(menu), cx);
3206 } else if this.completion_tasks.is_empty() {
3207 // If there are no more completion tasks and the last menu was
3208 // empty, we should hide it. If it was already hidden, we should
3209 // also show the copilot suggestion when available.
3210 if this.hide_context_menu(cx).is_none() {
3211 this.update_visible_copilot_suggestion(cx);
3212 }
3213 }
3214 })?;
3215
3216 Ok::<_, anyhow::Error>(())
3217 }
3218 .log_err()
3219 });
3220 self.completion_tasks.push((id, task));
3221 }
3222
3223 pub fn confirm_completion(
3224 &mut self,
3225 action: &ConfirmCompletion,
3226 cx: &mut ViewContext<Self>,
3227 ) -> Option<Task<Result<()>>> {
3228 use language::ToOffset as _;
3229
3230 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3231 menu
3232 } else {
3233 return None;
3234 };
3235
3236 let mat = completions_menu
3237 .matches
3238 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3239 let buffer_handle = completions_menu.buffer;
3240 let completion = completions_menu.completions.get(mat.candidate_id)?;
3241
3242 let snippet;
3243 let text;
3244 if completion.is_snippet() {
3245 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3246 text = snippet.as_ref().unwrap().text.clone();
3247 } else {
3248 snippet = None;
3249 text = completion.new_text.clone();
3250 };
3251 let selections = self.selections.all::<usize>(cx);
3252 let buffer = buffer_handle.read(cx);
3253 let old_range = completion.old_range.to_offset(buffer);
3254 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3255
3256 let newest_selection = self.selections.newest_anchor();
3257 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3258 return None;
3259 }
3260
3261 let lookbehind = newest_selection
3262 .start
3263 .text_anchor
3264 .to_offset(buffer)
3265 .saturating_sub(old_range.start);
3266 let lookahead = old_range
3267 .end
3268 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3269 let mut common_prefix_len = old_text
3270 .bytes()
3271 .zip(text.bytes())
3272 .take_while(|(a, b)| a == b)
3273 .count();
3274
3275 let snapshot = self.buffer.read(cx).snapshot(cx);
3276 let mut range_to_replace: Option<Range<isize>> = None;
3277 let mut ranges = Vec::new();
3278 for selection in &selections {
3279 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3280 let start = selection.start.saturating_sub(lookbehind);
3281 let end = selection.end + lookahead;
3282 if selection.id == newest_selection.id {
3283 range_to_replace = Some(
3284 ((start + common_prefix_len) as isize - selection.start as isize)
3285 ..(end as isize - selection.start as isize),
3286 );
3287 }
3288 ranges.push(start + common_prefix_len..end);
3289 } else {
3290 common_prefix_len = 0;
3291 ranges.clear();
3292 ranges.extend(selections.iter().map(|s| {
3293 if s.id == newest_selection.id {
3294 range_to_replace = Some(
3295 old_range.start.to_offset_utf16(&snapshot).0 as isize
3296 - selection.start as isize
3297 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3298 - selection.start as isize,
3299 );
3300 old_range.clone()
3301 } else {
3302 s.start..s.end
3303 }
3304 }));
3305 break;
3306 }
3307 }
3308 let text = &text[common_prefix_len..];
3309
3310 cx.emit(Event::InputHandled {
3311 utf16_range_to_replace: range_to_replace,
3312 text: text.into(),
3313 });
3314
3315 self.transact(cx, |this, cx| {
3316 if let Some(mut snippet) = snippet {
3317 snippet.text = text.to_string();
3318 for tabstop in snippet.tabstops.iter_mut().flatten() {
3319 tabstop.start -= common_prefix_len as isize;
3320 tabstop.end -= common_prefix_len as isize;
3321 }
3322
3323 this.insert_snippet(&ranges, snippet, cx).log_err();
3324 } else {
3325 this.buffer.update(cx, |buffer, cx| {
3326 buffer.edit(
3327 ranges.iter().map(|range| (range.clone(), text)),
3328 this.autoindent_mode.clone(),
3329 cx,
3330 );
3331 });
3332 }
3333
3334 this.refresh_copilot_suggestions(true, cx);
3335 });
3336
3337 let project = self.project.clone()?;
3338 let apply_edits = project.update(cx, |project, cx| {
3339 project.apply_additional_edits_for_completion(
3340 buffer_handle,
3341 completion.clone(),
3342 true,
3343 cx,
3344 )
3345 });
3346 Some(cx.foreground().spawn(async move {
3347 apply_edits.await?;
3348 Ok(())
3349 }))
3350 }
3351
3352 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3353 if matches!(
3354 self.context_menu.as_ref(),
3355 Some(ContextMenu::CodeActions(_))
3356 ) {
3357 self.context_menu.take();
3358 cx.notify();
3359 return;
3360 }
3361
3362 let deployed_from_indicator = action.deployed_from_indicator;
3363 let mut task = self.code_actions_task.take();
3364 cx.spawn(|this, mut cx| async move {
3365 while let Some(prev_task) = task {
3366 prev_task.await;
3367 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3368 }
3369
3370 this.update(&mut cx, |this, cx| {
3371 if this.focused {
3372 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3373 this.show_context_menu(
3374 ContextMenu::CodeActions(CodeActionsMenu {
3375 buffer,
3376 actions,
3377 selected_item: Default::default(),
3378 list: Default::default(),
3379 deployed_from_indicator,
3380 }),
3381 cx,
3382 );
3383 }
3384 }
3385 })?;
3386
3387 Ok::<_, anyhow::Error>(())
3388 })
3389 .detach_and_log_err(cx);
3390 }
3391
3392 pub fn confirm_code_action(
3393 workspace: &mut Workspace,
3394 action: &ConfirmCodeAction,
3395 cx: &mut ViewContext<Workspace>,
3396 ) -> Option<Task<Result<()>>> {
3397 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3398 let actions_menu = if let ContextMenu::CodeActions(menu) =
3399 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3400 {
3401 menu
3402 } else {
3403 return None;
3404 };
3405 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3406 let action = actions_menu.actions.get(action_ix)?.clone();
3407 let title = action.lsp_action.title.clone();
3408 let buffer = actions_menu.buffer;
3409
3410 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3411 project.apply_code_action(buffer, action, true, cx)
3412 });
3413 let editor = editor.downgrade();
3414 Some(cx.spawn(|workspace, cx| async move {
3415 let project_transaction = apply_code_actions.await?;
3416 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3417 }))
3418 }
3419
3420 async fn open_project_transaction(
3421 this: &WeakViewHandle<Editor>,
3422 workspace: WeakViewHandle<Workspace>,
3423 transaction: ProjectTransaction,
3424 title: String,
3425 mut cx: AsyncAppContext,
3426 ) -> Result<()> {
3427 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3428
3429 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3430 entries.sort_unstable_by_key(|(buffer, _)| {
3431 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3432 });
3433
3434 // If the project transaction's edits are all contained within this editor, then
3435 // avoid opening a new editor to display them.
3436
3437 if let Some((buffer, transaction)) = entries.first() {
3438 if entries.len() == 1 {
3439 let excerpt = this.read_with(&cx, |editor, cx| {
3440 editor
3441 .buffer()
3442 .read(cx)
3443 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3444 })?;
3445 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3446 if excerpted_buffer == *buffer {
3447 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3448 let excerpt_range = excerpt_range.to_offset(buffer);
3449 buffer
3450 .edited_ranges_for_transaction::<usize>(transaction)
3451 .all(|range| {
3452 excerpt_range.start <= range.start
3453 && excerpt_range.end >= range.end
3454 })
3455 });
3456
3457 if all_edits_within_excerpt {
3458 return Ok(());
3459 }
3460 }
3461 }
3462 }
3463 } else {
3464 return Ok(());
3465 }
3466
3467 let mut ranges_to_highlight = Vec::new();
3468 let excerpt_buffer = cx.add_model(|cx| {
3469 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3470 for (buffer_handle, transaction) in &entries {
3471 let buffer = buffer_handle.read(cx);
3472 ranges_to_highlight.extend(
3473 multibuffer.push_excerpts_with_context_lines(
3474 buffer_handle.clone(),
3475 buffer
3476 .edited_ranges_for_transaction::<usize>(transaction)
3477 .collect(),
3478 1,
3479 cx,
3480 ),
3481 );
3482 }
3483 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3484 multibuffer
3485 });
3486
3487 workspace.update(&mut cx, |workspace, cx| {
3488 let project = workspace.project().clone();
3489 let editor =
3490 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3491 workspace.add_item(Box::new(editor.clone()), cx);
3492 editor.update(cx, |editor, cx| {
3493 editor.highlight_background::<Self>(
3494 ranges_to_highlight,
3495 |theme| theme.editor.highlighted_line_background,
3496 cx,
3497 );
3498 });
3499 })?;
3500
3501 Ok(())
3502 }
3503
3504 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3505 let project = self.project.clone()?;
3506 let buffer = self.buffer.read(cx);
3507 let newest_selection = self.selections.newest_anchor().clone();
3508 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3509 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3510 if start_buffer != end_buffer {
3511 return None;
3512 }
3513
3514 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3515 cx.background().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await;
3516
3517 let actions = project
3518 .update(&mut cx, |project, cx| {
3519 project.code_actions(&start_buffer, start..end, cx)
3520 })
3521 .await;
3522
3523 this.update(&mut cx, |this, cx| {
3524 this.available_code_actions = actions.log_err().and_then(|actions| {
3525 if actions.is_empty() {
3526 None
3527 } else {
3528 Some((start_buffer, actions.into()))
3529 }
3530 });
3531 cx.notify();
3532 })
3533 .log_err();
3534 }));
3535 None
3536 }
3537
3538 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3539 if self.pending_rename.is_some() {
3540 return None;
3541 }
3542
3543 let project = self.project.clone()?;
3544 let buffer = self.buffer.read(cx);
3545 let newest_selection = self.selections.newest_anchor().clone();
3546 let cursor_position = newest_selection.head();
3547 let (cursor_buffer, cursor_buffer_position) =
3548 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3549 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3550 if cursor_buffer != tail_buffer {
3551 return None;
3552 }
3553
3554 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3555 cx.background()
3556 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3557 .await;
3558
3559 let highlights = project
3560 .update(&mut cx, |project, cx| {
3561 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3562 })
3563 .await
3564 .log_err();
3565
3566 if let Some(highlights) = highlights {
3567 this.update(&mut cx, |this, cx| {
3568 if this.pending_rename.is_some() {
3569 return;
3570 }
3571
3572 let buffer_id = cursor_position.buffer_id;
3573 let buffer = this.buffer.read(cx);
3574 if !buffer
3575 .text_anchor_for_position(cursor_position, cx)
3576 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3577 {
3578 return;
3579 }
3580
3581 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3582 let mut write_ranges = Vec::new();
3583 let mut read_ranges = Vec::new();
3584 for highlight in highlights {
3585 for (excerpt_id, excerpt_range) in
3586 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3587 {
3588 let start = highlight
3589 .range
3590 .start
3591 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3592 let end = highlight
3593 .range
3594 .end
3595 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3596 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3597 continue;
3598 }
3599
3600 let range = Anchor {
3601 buffer_id,
3602 excerpt_id: excerpt_id.clone(),
3603 text_anchor: start,
3604 }..Anchor {
3605 buffer_id,
3606 excerpt_id,
3607 text_anchor: end,
3608 };
3609 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3610 write_ranges.push(range);
3611 } else {
3612 read_ranges.push(range);
3613 }
3614 }
3615 }
3616
3617 this.highlight_background::<DocumentHighlightRead>(
3618 read_ranges,
3619 |theme| theme.editor.document_highlight_read_background,
3620 cx,
3621 );
3622 this.highlight_background::<DocumentHighlightWrite>(
3623 write_ranges,
3624 |theme| theme.editor.document_highlight_write_background,
3625 cx,
3626 );
3627 cx.notify();
3628 })
3629 .log_err();
3630 }
3631 }));
3632 None
3633 }
3634
3635 fn refresh_copilot_suggestions(
3636 &mut self,
3637 debounce: bool,
3638 cx: &mut ViewContext<Self>,
3639 ) -> Option<()> {
3640 let copilot = Copilot::global(cx)?;
3641 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3642 self.clear_copilot_suggestions(cx);
3643 return None;
3644 }
3645 self.update_visible_copilot_suggestion(cx);
3646
3647 let snapshot = self.buffer.read(cx).snapshot(cx);
3648 let cursor = self.selections.newest_anchor().head();
3649 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3650 self.clear_copilot_suggestions(cx);
3651 return None;
3652 }
3653
3654 let (buffer, buffer_position) =
3655 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3656 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3657 if debounce {
3658 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3659 }
3660
3661 let completions = copilot
3662 .update(&mut cx, |copilot, cx| {
3663 copilot.completions(&buffer, buffer_position, cx)
3664 })
3665 .await
3666 .log_err()
3667 .into_iter()
3668 .flatten()
3669 .collect_vec();
3670
3671 this.update(&mut cx, |this, cx| {
3672 if !completions.is_empty() {
3673 this.copilot_state.cycled = false;
3674 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3675 this.copilot_state.completions.clear();
3676 this.copilot_state.active_completion_index = 0;
3677 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3678 for completion in completions {
3679 this.copilot_state.push_completion(completion);
3680 }
3681 this.update_visible_copilot_suggestion(cx);
3682 }
3683 })
3684 .log_err()?;
3685 Some(())
3686 });
3687
3688 Some(())
3689 }
3690
3691 fn cycle_copilot_suggestions(
3692 &mut self,
3693 direction: Direction,
3694 cx: &mut ViewContext<Self>,
3695 ) -> Option<()> {
3696 let copilot = Copilot::global(cx)?;
3697 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3698 return None;
3699 }
3700
3701 if self.copilot_state.cycled {
3702 self.copilot_state.cycle_completions(direction);
3703 self.update_visible_copilot_suggestion(cx);
3704 } else {
3705 let cursor = self.selections.newest_anchor().head();
3706 let (buffer, buffer_position) =
3707 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3708 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3709 let completions = copilot
3710 .update(&mut cx, |copilot, cx| {
3711 copilot.completions_cycling(&buffer, buffer_position, cx)
3712 })
3713 .await;
3714
3715 this.update(&mut cx, |this, cx| {
3716 this.copilot_state.cycled = true;
3717 for completion in completions.log_err().into_iter().flatten() {
3718 this.copilot_state.push_completion(completion);
3719 }
3720 this.copilot_state.cycle_completions(direction);
3721 this.update_visible_copilot_suggestion(cx);
3722 })
3723 .log_err()?;
3724
3725 Some(())
3726 });
3727 }
3728
3729 Some(())
3730 }
3731
3732 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3733 if !self.has_active_copilot_suggestion(cx) {
3734 self.refresh_copilot_suggestions(false, cx);
3735 return;
3736 }
3737
3738 self.update_visible_copilot_suggestion(cx);
3739 }
3740
3741 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3742 if self.has_active_copilot_suggestion(cx) {
3743 self.cycle_copilot_suggestions(Direction::Next, cx);
3744 } else {
3745 self.refresh_copilot_suggestions(false, cx);
3746 }
3747 }
3748
3749 fn previous_copilot_suggestion(
3750 &mut self,
3751 _: &copilot::PreviousSuggestion,
3752 cx: &mut ViewContext<Self>,
3753 ) {
3754 if self.has_active_copilot_suggestion(cx) {
3755 self.cycle_copilot_suggestions(Direction::Prev, cx);
3756 } else {
3757 self.refresh_copilot_suggestions(false, cx);
3758 }
3759 }
3760
3761 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3762 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3763 if let Some((copilot, completion)) =
3764 Copilot::global(cx).zip(self.copilot_state.active_completion())
3765 {
3766 copilot
3767 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3768 .detach_and_log_err(cx);
3769
3770 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3771 }
3772 cx.emit(Event::InputHandled {
3773 utf16_range_to_replace: None,
3774 text: suggestion.text.to_string().into(),
3775 });
3776 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3777 cx.notify();
3778 true
3779 } else {
3780 false
3781 }
3782 }
3783
3784 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3785 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3786 if let Some(copilot) = Copilot::global(cx) {
3787 copilot
3788 .update(cx, |copilot, cx| {
3789 copilot.discard_completions(&self.copilot_state.completions, cx)
3790 })
3791 .detach_and_log_err(cx);
3792
3793 self.report_copilot_event(None, false, cx)
3794 }
3795
3796 self.display_map.update(cx, |map, cx| {
3797 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3798 });
3799 cx.notify();
3800 true
3801 } else {
3802 false
3803 }
3804 }
3805
3806 fn is_copilot_enabled_at(
3807 &self,
3808 location: Anchor,
3809 snapshot: &MultiBufferSnapshot,
3810 cx: &mut ViewContext<Self>,
3811 ) -> bool {
3812 let file = snapshot.file_at(location);
3813 let language = snapshot.language_at(location);
3814 let settings = all_language_settings(file, cx);
3815 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3816 }
3817
3818 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3819 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3820 let buffer = self.buffer.read(cx).read(cx);
3821 suggestion.position.is_valid(&buffer)
3822 } else {
3823 false
3824 }
3825 }
3826
3827 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3828 let suggestion = self.copilot_state.suggestion.take()?;
3829 self.display_map.update(cx, |map, cx| {
3830 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3831 });
3832 let buffer = self.buffer.read(cx).read(cx);
3833
3834 if suggestion.position.is_valid(&buffer) {
3835 Some(suggestion)
3836 } else {
3837 None
3838 }
3839 }
3840
3841 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3842 let snapshot = self.buffer.read(cx).snapshot(cx);
3843 let selection = self.selections.newest_anchor();
3844 let cursor = selection.head();
3845
3846 if self.context_menu.is_some()
3847 || !self.completion_tasks.is_empty()
3848 || selection.start != selection.end
3849 {
3850 self.discard_copilot_suggestion(cx);
3851 } else if let Some(text) = self
3852 .copilot_state
3853 .text_for_active_completion(cursor, &snapshot)
3854 {
3855 let text = Rope::from(text);
3856 let mut to_remove = Vec::new();
3857 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3858 to_remove.push(suggestion.id);
3859 }
3860
3861 let suggestion_inlay =
3862 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3863 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3864 self.display_map.update(cx, move |map, cx| {
3865 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3866 });
3867 cx.notify();
3868 } else {
3869 self.discard_copilot_suggestion(cx);
3870 }
3871 }
3872
3873 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3874 self.copilot_state = Default::default();
3875 self.discard_copilot_suggestion(cx);
3876 }
3877
3878 pub fn render_code_actions_indicator(
3879 &self,
3880 style: &EditorStyle,
3881 is_active: bool,
3882 cx: &mut ViewContext<Self>,
3883 ) -> Option<AnyElement<Self>> {
3884 if self.available_code_actions.is_some() {
3885 enum CodeActions {}
3886 Some(
3887 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
3888 Svg::new("icons/bolt.svg").with_color(
3889 style
3890 .code_actions
3891 .indicator
3892 .in_state(is_active)
3893 .style_for(state)
3894 .color,
3895 )
3896 })
3897 .with_cursor_style(CursorStyle::PointingHand)
3898 .with_padding(Padding::uniform(3.))
3899 .on_down(MouseButton::Left, |_, this, cx| {
3900 this.toggle_code_actions(
3901 &ToggleCodeActions {
3902 deployed_from_indicator: true,
3903 },
3904 cx,
3905 );
3906 })
3907 .into_any(),
3908 )
3909 } else {
3910 None
3911 }
3912 }
3913
3914 pub fn render_fold_indicators(
3915 &self,
3916 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3917 style: &EditorStyle,
3918 gutter_hovered: bool,
3919 line_height: f32,
3920 gutter_margin: f32,
3921 cx: &mut ViewContext<Self>,
3922 ) -> Vec<Option<AnyElement<Self>>> {
3923 enum FoldIndicators {}
3924
3925 let style = style.folds.clone();
3926
3927 fold_data
3928 .iter()
3929 .enumerate()
3930 .map(|(ix, fold_data)| {
3931 fold_data
3932 .map(|(fold_status, buffer_row, active)| {
3933 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3934 MouseEventHandler::new::<FoldIndicators, _>(
3935 ix as usize,
3936 cx,
3937 |mouse_state, _| {
3938 Svg::new(match fold_status {
3939 FoldStatus::Folded => style.folded_icon.clone(),
3940 FoldStatus::Foldable => style.foldable_icon.clone(),
3941 })
3942 .with_color(
3943 style
3944 .indicator
3945 .in_state(fold_status == FoldStatus::Folded)
3946 .style_for(mouse_state)
3947 .color,
3948 )
3949 .constrained()
3950 .with_width(gutter_margin * style.icon_margin_scale)
3951 .aligned()
3952 .constrained()
3953 .with_height(line_height)
3954 .with_width(gutter_margin)
3955 .aligned()
3956 },
3957 )
3958 .with_cursor_style(CursorStyle::PointingHand)
3959 .with_padding(Padding::uniform(3.))
3960 .on_click(MouseButton::Left, {
3961 move |_, editor, cx| match fold_status {
3962 FoldStatus::Folded => {
3963 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3964 }
3965 FoldStatus::Foldable => {
3966 editor.fold_at(&FoldAt { buffer_row }, cx);
3967 }
3968 }
3969 })
3970 .into_any()
3971 })
3972 })
3973 .flatten()
3974 })
3975 .collect()
3976 }
3977
3978 pub fn context_menu_visible(&self) -> bool {
3979 self.context_menu
3980 .as_ref()
3981 .map_or(false, |menu| menu.visible())
3982 }
3983
3984 pub fn render_context_menu(
3985 &self,
3986 cursor_position: DisplayPoint,
3987 style: EditorStyle,
3988 cx: &mut ViewContext<Editor>,
3989 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3990 self.context_menu
3991 .as_ref()
3992 .map(|menu| menu.render(cursor_position, style, cx))
3993 }
3994
3995 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3996 if !matches!(menu, ContextMenu::Completions(_)) {
3997 self.completion_tasks.clear();
3998 }
3999 self.context_menu = Some(menu);
4000 self.discard_copilot_suggestion(cx);
4001 cx.notify();
4002 }
4003
4004 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4005 cx.notify();
4006 self.completion_tasks.clear();
4007 let context_menu = self.context_menu.take();
4008 if context_menu.is_some() {
4009 self.update_visible_copilot_suggestion(cx);
4010 }
4011 context_menu
4012 }
4013
4014 pub fn insert_snippet(
4015 &mut self,
4016 insertion_ranges: &[Range<usize>],
4017 snippet: Snippet,
4018 cx: &mut ViewContext<Self>,
4019 ) -> Result<()> {
4020 let tabstops = self.buffer.update(cx, |buffer, cx| {
4021 let snippet_text: Arc<str> = snippet.text.clone().into();
4022 buffer.edit(
4023 insertion_ranges
4024 .iter()
4025 .cloned()
4026 .map(|range| (range, snippet_text.clone())),
4027 Some(AutoindentMode::EachLine),
4028 cx,
4029 );
4030
4031 let snapshot = &*buffer.read(cx);
4032 let snippet = &snippet;
4033 snippet
4034 .tabstops
4035 .iter()
4036 .map(|tabstop| {
4037 let mut tabstop_ranges = tabstop
4038 .iter()
4039 .flat_map(|tabstop_range| {
4040 let mut delta = 0_isize;
4041 insertion_ranges.iter().map(move |insertion_range| {
4042 let insertion_start = insertion_range.start as isize + delta;
4043 delta +=
4044 snippet.text.len() as isize - insertion_range.len() as isize;
4045
4046 let start = snapshot.anchor_before(
4047 (insertion_start + tabstop_range.start) as usize,
4048 );
4049 let end = snapshot
4050 .anchor_after((insertion_start + tabstop_range.end) as usize);
4051 start..end
4052 })
4053 })
4054 .collect::<Vec<_>>();
4055 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4056 tabstop_ranges
4057 })
4058 .collect::<Vec<_>>()
4059 });
4060
4061 if let Some(tabstop) = tabstops.first() {
4062 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4063 s.select_ranges(tabstop.iter().cloned());
4064 });
4065 self.snippet_stack.push(SnippetState {
4066 active_index: 0,
4067 ranges: tabstops,
4068 });
4069 }
4070
4071 Ok(())
4072 }
4073
4074 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4075 self.move_to_snippet_tabstop(Bias::Right, cx)
4076 }
4077
4078 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4079 self.move_to_snippet_tabstop(Bias::Left, cx)
4080 }
4081
4082 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4083 if let Some(mut snippet) = self.snippet_stack.pop() {
4084 match bias {
4085 Bias::Left => {
4086 if snippet.active_index > 0 {
4087 snippet.active_index -= 1;
4088 } else {
4089 self.snippet_stack.push(snippet);
4090 return false;
4091 }
4092 }
4093 Bias::Right => {
4094 if snippet.active_index + 1 < snippet.ranges.len() {
4095 snippet.active_index += 1;
4096 } else {
4097 self.snippet_stack.push(snippet);
4098 return false;
4099 }
4100 }
4101 }
4102 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4103 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4104 s.select_anchor_ranges(current_ranges.iter().cloned())
4105 });
4106 // If snippet state is not at the last tabstop, push it back on the stack
4107 if snippet.active_index + 1 < snippet.ranges.len() {
4108 self.snippet_stack.push(snippet);
4109 }
4110 return true;
4111 }
4112 }
4113
4114 false
4115 }
4116
4117 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4118 self.transact(cx, |this, cx| {
4119 this.select_all(&SelectAll, cx);
4120 this.insert("", cx);
4121 });
4122 }
4123
4124 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4125 self.transact(cx, |this, cx| {
4126 this.select_autoclose_pair(cx);
4127 let mut selections = this.selections.all::<Point>(cx);
4128 if !this.selections.line_mode {
4129 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4130 for selection in &mut selections {
4131 if selection.is_empty() {
4132 let old_head = selection.head();
4133 let mut new_head =
4134 movement::left(&display_map, old_head.to_display_point(&display_map))
4135 .to_point(&display_map);
4136 if let Some((buffer, line_buffer_range)) = display_map
4137 .buffer_snapshot
4138 .buffer_line_for_row(old_head.row)
4139 {
4140 let indent_size =
4141 buffer.indent_size_for_line(line_buffer_range.start.row);
4142 let indent_len = match indent_size.kind {
4143 IndentKind::Space => {
4144 buffer.settings_at(line_buffer_range.start, cx).tab_size
4145 }
4146 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4147 };
4148 if old_head.column <= indent_size.len && old_head.column > 0 {
4149 let indent_len = indent_len.get();
4150 new_head = cmp::min(
4151 new_head,
4152 Point::new(
4153 old_head.row,
4154 ((old_head.column - 1) / indent_len) * indent_len,
4155 ),
4156 );
4157 }
4158 }
4159
4160 selection.set_head(new_head, SelectionGoal::None);
4161 }
4162 }
4163 }
4164
4165 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4166 this.insert("", cx);
4167 this.refresh_copilot_suggestions(true, cx);
4168 });
4169 }
4170
4171 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4172 self.transact(cx, |this, cx| {
4173 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4174 let line_mode = s.line_mode;
4175 s.move_with(|map, selection| {
4176 if selection.is_empty() && !line_mode {
4177 let cursor = movement::right(map, selection.head());
4178 selection.end = cursor;
4179 selection.reversed = true;
4180 selection.goal = SelectionGoal::None;
4181 }
4182 })
4183 });
4184 this.insert("", cx);
4185 this.refresh_copilot_suggestions(true, cx);
4186 });
4187 }
4188
4189 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4190 if self.move_to_prev_snippet_tabstop(cx) {
4191 return;
4192 }
4193
4194 self.outdent(&Outdent, cx);
4195 }
4196
4197 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4198 if self.move_to_next_snippet_tabstop(cx) {
4199 return;
4200 }
4201
4202 let mut selections = self.selections.all_adjusted(cx);
4203 let buffer = self.buffer.read(cx);
4204 let snapshot = buffer.snapshot(cx);
4205 let rows_iter = selections.iter().map(|s| s.head().row);
4206 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4207
4208 let mut edits = Vec::new();
4209 let mut prev_edited_row = 0;
4210 let mut row_delta = 0;
4211 for selection in &mut selections {
4212 if selection.start.row != prev_edited_row {
4213 row_delta = 0;
4214 }
4215 prev_edited_row = selection.end.row;
4216
4217 // If the selection is non-empty, then increase the indentation of the selected lines.
4218 if !selection.is_empty() {
4219 row_delta =
4220 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4221 continue;
4222 }
4223
4224 // If the selection is empty and the cursor is in the leading whitespace before the
4225 // suggested indentation, then auto-indent the line.
4226 let cursor = selection.head();
4227 let current_indent = snapshot.indent_size_for_line(cursor.row);
4228 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4229 if cursor.column < suggested_indent.len
4230 && cursor.column <= current_indent.len
4231 && current_indent.len <= suggested_indent.len
4232 {
4233 selection.start = Point::new(cursor.row, suggested_indent.len);
4234 selection.end = selection.start;
4235 if row_delta == 0 {
4236 edits.extend(Buffer::edit_for_indent_size_adjustment(
4237 cursor.row,
4238 current_indent,
4239 suggested_indent,
4240 ));
4241 row_delta = suggested_indent.len - current_indent.len;
4242 }
4243 continue;
4244 }
4245 }
4246
4247 // Accept copilot suggestion if there is only one selection and the cursor is not
4248 // in the leading whitespace.
4249 if self.selections.count() == 1
4250 && cursor.column >= current_indent.len
4251 && self.has_active_copilot_suggestion(cx)
4252 {
4253 self.accept_copilot_suggestion(cx);
4254 return;
4255 }
4256
4257 // Otherwise, insert a hard or soft tab.
4258 let settings = buffer.settings_at(cursor, cx);
4259 let tab_size = if settings.hard_tabs {
4260 IndentSize::tab()
4261 } else {
4262 let tab_size = settings.tab_size.get();
4263 let char_column = snapshot
4264 .text_for_range(Point::new(cursor.row, 0)..cursor)
4265 .flat_map(str::chars)
4266 .count()
4267 + row_delta as usize;
4268 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4269 IndentSize::spaces(chars_to_next_tab_stop)
4270 };
4271 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4272 selection.end = selection.start;
4273 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4274 row_delta += tab_size.len;
4275 }
4276
4277 self.transact(cx, |this, cx| {
4278 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4279 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4280 this.refresh_copilot_suggestions(true, cx);
4281 });
4282 }
4283
4284 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4285 let mut selections = self.selections.all::<Point>(cx);
4286 let mut prev_edited_row = 0;
4287 let mut row_delta = 0;
4288 let mut edits = Vec::new();
4289 let buffer = self.buffer.read(cx);
4290 let snapshot = buffer.snapshot(cx);
4291 for selection in &mut selections {
4292 if selection.start.row != prev_edited_row {
4293 row_delta = 0;
4294 }
4295 prev_edited_row = selection.end.row;
4296
4297 row_delta =
4298 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4299 }
4300
4301 self.transact(cx, |this, cx| {
4302 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4303 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4304 });
4305 }
4306
4307 fn indent_selection(
4308 buffer: &MultiBuffer,
4309 snapshot: &MultiBufferSnapshot,
4310 selection: &mut Selection<Point>,
4311 edits: &mut Vec<(Range<Point>, String)>,
4312 delta_for_start_row: u32,
4313 cx: &AppContext,
4314 ) -> u32 {
4315 let settings = buffer.settings_at(selection.start, cx);
4316 let tab_size = settings.tab_size.get();
4317 let indent_kind = if settings.hard_tabs {
4318 IndentKind::Tab
4319 } else {
4320 IndentKind::Space
4321 };
4322 let mut start_row = selection.start.row;
4323 let mut end_row = selection.end.row + 1;
4324
4325 // If a selection ends at the beginning of a line, don't indent
4326 // that last line.
4327 if selection.end.column == 0 {
4328 end_row -= 1;
4329 }
4330
4331 // Avoid re-indenting a row that has already been indented by a
4332 // previous selection, but still update this selection's column
4333 // to reflect that indentation.
4334 if delta_for_start_row > 0 {
4335 start_row += 1;
4336 selection.start.column += delta_for_start_row;
4337 if selection.end.row == selection.start.row {
4338 selection.end.column += delta_for_start_row;
4339 }
4340 }
4341
4342 let mut delta_for_end_row = 0;
4343 for row in start_row..end_row {
4344 let current_indent = snapshot.indent_size_for_line(row);
4345 let indent_delta = match (current_indent.kind, indent_kind) {
4346 (IndentKind::Space, IndentKind::Space) => {
4347 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4348 IndentSize::spaces(columns_to_next_tab_stop)
4349 }
4350 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4351 (_, IndentKind::Tab) => IndentSize::tab(),
4352 };
4353
4354 let row_start = Point::new(row, 0);
4355 edits.push((
4356 row_start..row_start,
4357 indent_delta.chars().collect::<String>(),
4358 ));
4359
4360 // Update this selection's endpoints to reflect the indentation.
4361 if row == selection.start.row {
4362 selection.start.column += indent_delta.len;
4363 }
4364 if row == selection.end.row {
4365 selection.end.column += indent_delta.len;
4366 delta_for_end_row = indent_delta.len;
4367 }
4368 }
4369
4370 if selection.start.row == selection.end.row {
4371 delta_for_start_row + delta_for_end_row
4372 } else {
4373 delta_for_end_row
4374 }
4375 }
4376
4377 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4378 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4379 let selections = self.selections.all::<Point>(cx);
4380 let mut deletion_ranges = Vec::new();
4381 let mut last_outdent = None;
4382 {
4383 let buffer = self.buffer.read(cx);
4384 let snapshot = buffer.snapshot(cx);
4385 for selection in &selections {
4386 let settings = buffer.settings_at(selection.start, cx);
4387 let tab_size = settings.tab_size.get();
4388 let mut rows = selection.spanned_rows(false, &display_map);
4389
4390 // Avoid re-outdenting a row that has already been outdented by a
4391 // previous selection.
4392 if let Some(last_row) = last_outdent {
4393 if last_row == rows.start {
4394 rows.start += 1;
4395 }
4396 }
4397
4398 for row in rows {
4399 let indent_size = snapshot.indent_size_for_line(row);
4400 if indent_size.len > 0 {
4401 let deletion_len = match indent_size.kind {
4402 IndentKind::Space => {
4403 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4404 if columns_to_prev_tab_stop == 0 {
4405 tab_size
4406 } else {
4407 columns_to_prev_tab_stop
4408 }
4409 }
4410 IndentKind::Tab => 1,
4411 };
4412 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4413 last_outdent = Some(row);
4414 }
4415 }
4416 }
4417 }
4418
4419 self.transact(cx, |this, cx| {
4420 this.buffer.update(cx, |buffer, cx| {
4421 let empty_str: Arc<str> = "".into();
4422 buffer.edit(
4423 deletion_ranges
4424 .into_iter()
4425 .map(|range| (range, empty_str.clone())),
4426 None,
4427 cx,
4428 );
4429 });
4430 let selections = this.selections.all::<usize>(cx);
4431 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4432 });
4433 }
4434
4435 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4436 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4437 let selections = self.selections.all::<Point>(cx);
4438
4439 let mut new_cursors = Vec::new();
4440 let mut edit_ranges = Vec::new();
4441 let mut selections = selections.iter().peekable();
4442 while let Some(selection) = selections.next() {
4443 let mut rows = selection.spanned_rows(false, &display_map);
4444 let goal_display_column = selection.head().to_display_point(&display_map).column();
4445
4446 // Accumulate contiguous regions of rows that we want to delete.
4447 while let Some(next_selection) = selections.peek() {
4448 let next_rows = next_selection.spanned_rows(false, &display_map);
4449 if next_rows.start <= rows.end {
4450 rows.end = next_rows.end;
4451 selections.next().unwrap();
4452 } else {
4453 break;
4454 }
4455 }
4456
4457 let buffer = &display_map.buffer_snapshot;
4458 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4459 let edit_end;
4460 let cursor_buffer_row;
4461 if buffer.max_point().row >= rows.end {
4462 // If there's a line after the range, delete the \n from the end of the row range
4463 // and position the cursor on the next line.
4464 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4465 cursor_buffer_row = rows.end;
4466 } else {
4467 // If there isn't a line after the range, delete the \n from the line before the
4468 // start of the row range and position the cursor there.
4469 edit_start = edit_start.saturating_sub(1);
4470 edit_end = buffer.len();
4471 cursor_buffer_row = rows.start.saturating_sub(1);
4472 }
4473
4474 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4475 *cursor.column_mut() =
4476 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4477
4478 new_cursors.push((
4479 selection.id,
4480 buffer.anchor_after(cursor.to_point(&display_map)),
4481 ));
4482 edit_ranges.push(edit_start..edit_end);
4483 }
4484
4485 self.transact(cx, |this, cx| {
4486 let buffer = this.buffer.update(cx, |buffer, cx| {
4487 let empty_str: Arc<str> = "".into();
4488 buffer.edit(
4489 edit_ranges
4490 .into_iter()
4491 .map(|range| (range, empty_str.clone())),
4492 None,
4493 cx,
4494 );
4495 buffer.snapshot(cx)
4496 });
4497 let new_selections = new_cursors
4498 .into_iter()
4499 .map(|(id, cursor)| {
4500 let cursor = cursor.to_point(&buffer);
4501 Selection {
4502 id,
4503 start: cursor,
4504 end: cursor,
4505 reversed: false,
4506 goal: SelectionGoal::None,
4507 }
4508 })
4509 .collect();
4510
4511 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4512 s.select(new_selections);
4513 });
4514 });
4515 }
4516
4517 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4518 let mut row_ranges = Vec::<Range<u32>>::new();
4519 for selection in self.selections.all::<Point>(cx) {
4520 let start = selection.start.row;
4521 let end = if selection.start.row == selection.end.row {
4522 selection.start.row + 1
4523 } else {
4524 selection.end.row
4525 };
4526
4527 if let Some(last_row_range) = row_ranges.last_mut() {
4528 if start <= last_row_range.end {
4529 last_row_range.end = end;
4530 continue;
4531 }
4532 }
4533 row_ranges.push(start..end);
4534 }
4535
4536 let snapshot = self.buffer.read(cx).snapshot(cx);
4537 let mut cursor_positions = Vec::new();
4538 for row_range in &row_ranges {
4539 let anchor = snapshot.anchor_before(Point::new(
4540 row_range.end - 1,
4541 snapshot.line_len(row_range.end - 1),
4542 ));
4543 cursor_positions.push(anchor.clone()..anchor);
4544 }
4545
4546 self.transact(cx, |this, cx| {
4547 for row_range in row_ranges.into_iter().rev() {
4548 for row in row_range.rev() {
4549 let end_of_line = Point::new(row, snapshot.line_len(row));
4550 let indent = snapshot.indent_size_for_line(row + 1);
4551 let start_of_next_line = Point::new(row + 1, indent.len);
4552
4553 let replace = if snapshot.line_len(row + 1) > indent.len {
4554 " "
4555 } else {
4556 ""
4557 };
4558
4559 this.buffer.update(cx, |buffer, cx| {
4560 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4561 });
4562 }
4563 }
4564
4565 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4566 s.select_anchor_ranges(cursor_positions)
4567 });
4568 });
4569 }
4570
4571 pub fn sort_lines_case_sensitive(
4572 &mut self,
4573 _: &SortLinesCaseSensitive,
4574 cx: &mut ViewContext<Self>,
4575 ) {
4576 self.manipulate_lines(cx, |lines| lines.sort())
4577 }
4578
4579 pub fn sort_lines_case_insensitive(
4580 &mut self,
4581 _: &SortLinesCaseInsensitive,
4582 cx: &mut ViewContext<Self>,
4583 ) {
4584 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4585 }
4586
4587 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4588 self.manipulate_lines(cx, |lines| lines.reverse())
4589 }
4590
4591 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4592 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4593 }
4594
4595 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4596 where
4597 Fn: FnMut(&mut [&str]),
4598 {
4599 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4600 let buffer = self.buffer.read(cx).snapshot(cx);
4601
4602 let mut edits = Vec::new();
4603
4604 let selections = self.selections.all::<Point>(cx);
4605 let mut selections = selections.iter().peekable();
4606 let mut contiguous_row_selections = Vec::new();
4607 let mut new_selections = Vec::new();
4608
4609 while let Some(selection) = selections.next() {
4610 let (start_row, end_row) = consume_contiguous_rows(
4611 &mut contiguous_row_selections,
4612 selection,
4613 &display_map,
4614 &mut selections,
4615 );
4616
4617 let start_point = Point::new(start_row, 0);
4618 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4619 let text = buffer
4620 .text_for_range(start_point..end_point)
4621 .collect::<String>();
4622 let mut lines = text.split("\n").collect_vec();
4623
4624 let lines_len = lines.len();
4625 callback(&mut lines);
4626
4627 // This is a current limitation with selections.
4628 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4629 debug_assert!(
4630 lines.len() == lines_len,
4631 "callback should not change the number of lines"
4632 );
4633
4634 edits.push((start_point..end_point, lines.join("\n")));
4635 let start_anchor = buffer.anchor_after(start_point);
4636 let end_anchor = buffer.anchor_before(end_point);
4637
4638 // Make selection and push
4639 new_selections.push(Selection {
4640 id: selection.id,
4641 start: start_anchor.to_offset(&buffer),
4642 end: end_anchor.to_offset(&buffer),
4643 goal: SelectionGoal::None,
4644 reversed: selection.reversed,
4645 });
4646 }
4647
4648 self.transact(cx, |this, cx| {
4649 this.buffer.update(cx, |buffer, cx| {
4650 buffer.edit(edits, None, cx);
4651 });
4652
4653 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4654 s.select(new_selections);
4655 });
4656
4657 this.request_autoscroll(Autoscroll::fit(), cx);
4658 });
4659 }
4660
4661 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4662 self.manipulate_text(cx, |text| text.to_uppercase())
4663 }
4664
4665 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4666 self.manipulate_text(cx, |text| text.to_lowercase())
4667 }
4668
4669 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4670 self.manipulate_text(cx, |text| {
4671 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4672 // https://github.com/rutrum/convert-case/issues/16
4673 text.split("\n")
4674 .map(|line| line.to_case(Case::Title))
4675 .join("\n")
4676 })
4677 }
4678
4679 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4680 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4681 }
4682
4683 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4684 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4685 }
4686
4687 pub fn convert_to_upper_camel_case(
4688 &mut self,
4689 _: &ConvertToUpperCamelCase,
4690 cx: &mut ViewContext<Self>,
4691 ) {
4692 self.manipulate_text(cx, |text| {
4693 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4694 // https://github.com/rutrum/convert-case/issues/16
4695 text.split("\n")
4696 .map(|line| line.to_case(Case::UpperCamel))
4697 .join("\n")
4698 })
4699 }
4700
4701 pub fn convert_to_lower_camel_case(
4702 &mut self,
4703 _: &ConvertToLowerCamelCase,
4704 cx: &mut ViewContext<Self>,
4705 ) {
4706 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4707 }
4708
4709 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4710 where
4711 Fn: FnMut(&str) -> String,
4712 {
4713 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4714 let buffer = self.buffer.read(cx).snapshot(cx);
4715
4716 let mut new_selections = Vec::new();
4717 let mut edits = Vec::new();
4718 let mut selection_adjustment = 0i32;
4719
4720 for selection in self.selections.all::<usize>(cx) {
4721 let selection_is_empty = selection.is_empty();
4722
4723 let (start, end) = if selection_is_empty {
4724 let word_range = movement::surrounding_word(
4725 &display_map,
4726 selection.start.to_display_point(&display_map),
4727 );
4728 let start = word_range.start.to_offset(&display_map, Bias::Left);
4729 let end = word_range.end.to_offset(&display_map, Bias::Left);
4730 (start, end)
4731 } else {
4732 (selection.start, selection.end)
4733 };
4734
4735 let text = buffer.text_for_range(start..end).collect::<String>();
4736 let old_length = text.len() as i32;
4737 let text = callback(&text);
4738
4739 new_selections.push(Selection {
4740 start: (start as i32 - selection_adjustment) as usize,
4741 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4742 goal: SelectionGoal::None,
4743 ..selection
4744 });
4745
4746 selection_adjustment += old_length - text.len() as i32;
4747
4748 edits.push((start..end, text));
4749 }
4750
4751 self.transact(cx, |this, cx| {
4752 this.buffer.update(cx, |buffer, cx| {
4753 buffer.edit(edits, None, cx);
4754 });
4755
4756 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4757 s.select(new_selections);
4758 });
4759
4760 this.request_autoscroll(Autoscroll::fit(), cx);
4761 });
4762 }
4763
4764 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4765 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4766 let buffer = &display_map.buffer_snapshot;
4767 let selections = self.selections.all::<Point>(cx);
4768
4769 let mut edits = Vec::new();
4770 let mut selections_iter = selections.iter().peekable();
4771 while let Some(selection) = selections_iter.next() {
4772 // Avoid duplicating the same lines twice.
4773 let mut rows = selection.spanned_rows(false, &display_map);
4774
4775 while let Some(next_selection) = selections_iter.peek() {
4776 let next_rows = next_selection.spanned_rows(false, &display_map);
4777 if next_rows.start < rows.end {
4778 rows.end = next_rows.end;
4779 selections_iter.next().unwrap();
4780 } else {
4781 break;
4782 }
4783 }
4784
4785 // Copy the text from the selected row region and splice it at the start of the region.
4786 let start = Point::new(rows.start, 0);
4787 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4788 let text = buffer
4789 .text_for_range(start..end)
4790 .chain(Some("\n"))
4791 .collect::<String>();
4792 edits.push((start..start, text));
4793 }
4794
4795 self.transact(cx, |this, cx| {
4796 this.buffer.update(cx, |buffer, cx| {
4797 buffer.edit(edits, None, cx);
4798 });
4799
4800 this.request_autoscroll(Autoscroll::fit(), cx);
4801 });
4802 }
4803
4804 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4805 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4806 let buffer = self.buffer.read(cx).snapshot(cx);
4807
4808 let mut edits = Vec::new();
4809 let mut unfold_ranges = Vec::new();
4810 let mut refold_ranges = Vec::new();
4811
4812 let selections = self.selections.all::<Point>(cx);
4813 let mut selections = selections.iter().peekable();
4814 let mut contiguous_row_selections = Vec::new();
4815 let mut new_selections = Vec::new();
4816
4817 while let Some(selection) = selections.next() {
4818 // Find all the selections that span a contiguous row range
4819 let (start_row, end_row) = consume_contiguous_rows(
4820 &mut contiguous_row_selections,
4821 selection,
4822 &display_map,
4823 &mut selections,
4824 );
4825
4826 // Move the text spanned by the row range to be before the line preceding the row range
4827 if start_row > 0 {
4828 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4829 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4830 let insertion_point = display_map
4831 .prev_line_boundary(Point::new(start_row - 1, 0))
4832 .0;
4833
4834 // Don't move lines across excerpts
4835 if buffer
4836 .excerpt_boundaries_in_range((
4837 Bound::Excluded(insertion_point),
4838 Bound::Included(range_to_move.end),
4839 ))
4840 .next()
4841 .is_none()
4842 {
4843 let text = buffer
4844 .text_for_range(range_to_move.clone())
4845 .flat_map(|s| s.chars())
4846 .skip(1)
4847 .chain(['\n'])
4848 .collect::<String>();
4849
4850 edits.push((
4851 buffer.anchor_after(range_to_move.start)
4852 ..buffer.anchor_before(range_to_move.end),
4853 String::new(),
4854 ));
4855 let insertion_anchor = buffer.anchor_after(insertion_point);
4856 edits.push((insertion_anchor..insertion_anchor, text));
4857
4858 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4859
4860 // Move selections up
4861 new_selections.extend(contiguous_row_selections.drain(..).map(
4862 |mut selection| {
4863 selection.start.row -= row_delta;
4864 selection.end.row -= row_delta;
4865 selection
4866 },
4867 ));
4868
4869 // Move folds up
4870 unfold_ranges.push(range_to_move.clone());
4871 for fold in display_map.folds_in_range(
4872 buffer.anchor_before(range_to_move.start)
4873 ..buffer.anchor_after(range_to_move.end),
4874 ) {
4875 let mut start = fold.start.to_point(&buffer);
4876 let mut end = fold.end.to_point(&buffer);
4877 start.row -= row_delta;
4878 end.row -= row_delta;
4879 refold_ranges.push(start..end);
4880 }
4881 }
4882 }
4883
4884 // If we didn't move line(s), preserve the existing selections
4885 new_selections.append(&mut contiguous_row_selections);
4886 }
4887
4888 self.transact(cx, |this, cx| {
4889 this.unfold_ranges(unfold_ranges, true, true, cx);
4890 this.buffer.update(cx, |buffer, cx| {
4891 for (range, text) in edits {
4892 buffer.edit([(range, text)], None, cx);
4893 }
4894 });
4895 this.fold_ranges(refold_ranges, true, cx);
4896 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4897 s.select(new_selections);
4898 })
4899 });
4900 }
4901
4902 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4903 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4904 let buffer = self.buffer.read(cx).snapshot(cx);
4905
4906 let mut edits = Vec::new();
4907 let mut unfold_ranges = Vec::new();
4908 let mut refold_ranges = Vec::new();
4909
4910 let selections = self.selections.all::<Point>(cx);
4911 let mut selections = selections.iter().peekable();
4912 let mut contiguous_row_selections = Vec::new();
4913 let mut new_selections = Vec::new();
4914
4915 while let Some(selection) = selections.next() {
4916 // Find all the selections that span a contiguous row range
4917 let (start_row, end_row) = consume_contiguous_rows(
4918 &mut contiguous_row_selections,
4919 selection,
4920 &display_map,
4921 &mut selections,
4922 );
4923
4924 // Move the text spanned by the row range to be after the last line of the row range
4925 if end_row <= buffer.max_point().row {
4926 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4927 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4928
4929 // Don't move lines across excerpt boundaries
4930 if buffer
4931 .excerpt_boundaries_in_range((
4932 Bound::Excluded(range_to_move.start),
4933 Bound::Included(insertion_point),
4934 ))
4935 .next()
4936 .is_none()
4937 {
4938 let mut text = String::from("\n");
4939 text.extend(buffer.text_for_range(range_to_move.clone()));
4940 text.pop(); // Drop trailing newline
4941 edits.push((
4942 buffer.anchor_after(range_to_move.start)
4943 ..buffer.anchor_before(range_to_move.end),
4944 String::new(),
4945 ));
4946 let insertion_anchor = buffer.anchor_after(insertion_point);
4947 edits.push((insertion_anchor..insertion_anchor, text));
4948
4949 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4950
4951 // Move selections down
4952 new_selections.extend(contiguous_row_selections.drain(..).map(
4953 |mut selection| {
4954 selection.start.row += row_delta;
4955 selection.end.row += row_delta;
4956 selection
4957 },
4958 ));
4959
4960 // Move folds down
4961 unfold_ranges.push(range_to_move.clone());
4962 for fold in display_map.folds_in_range(
4963 buffer.anchor_before(range_to_move.start)
4964 ..buffer.anchor_after(range_to_move.end),
4965 ) {
4966 let mut start = fold.start.to_point(&buffer);
4967 let mut end = fold.end.to_point(&buffer);
4968 start.row += row_delta;
4969 end.row += row_delta;
4970 refold_ranges.push(start..end);
4971 }
4972 }
4973 }
4974
4975 // If we didn't move line(s), preserve the existing selections
4976 new_selections.append(&mut contiguous_row_selections);
4977 }
4978
4979 self.transact(cx, |this, cx| {
4980 this.unfold_ranges(unfold_ranges, true, true, cx);
4981 this.buffer.update(cx, |buffer, cx| {
4982 for (range, text) in edits {
4983 buffer.edit([(range, text)], None, cx);
4984 }
4985 });
4986 this.fold_ranges(refold_ranges, true, cx);
4987 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4988 });
4989 }
4990
4991 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4992 self.transact(cx, |this, cx| {
4993 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4994 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4995 let line_mode = s.line_mode;
4996 s.move_with(|display_map, selection| {
4997 if !selection.is_empty() || line_mode {
4998 return;
4999 }
5000
5001 let mut head = selection.head();
5002 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5003 if head.column() == display_map.line_len(head.row()) {
5004 transpose_offset = display_map
5005 .buffer_snapshot
5006 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5007 }
5008
5009 if transpose_offset == 0 {
5010 return;
5011 }
5012
5013 *head.column_mut() += 1;
5014 head = display_map.clip_point(head, Bias::Right);
5015 selection.collapse_to(head, SelectionGoal::Column(head.column()));
5016
5017 let transpose_start = display_map
5018 .buffer_snapshot
5019 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5020 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5021 let transpose_end = display_map
5022 .buffer_snapshot
5023 .clip_offset(transpose_offset + 1, Bias::Right);
5024 if let Some(ch) =
5025 display_map.buffer_snapshot.chars_at(transpose_start).next()
5026 {
5027 edits.push((transpose_start..transpose_offset, String::new()));
5028 edits.push((transpose_end..transpose_end, ch.to_string()));
5029 }
5030 }
5031 });
5032 edits
5033 });
5034 this.buffer
5035 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5036 let selections = this.selections.all::<usize>(cx);
5037 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5038 s.select(selections);
5039 });
5040 });
5041 }
5042
5043 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5044 let mut text = String::new();
5045 let buffer = self.buffer.read(cx).snapshot(cx);
5046 let mut selections = self.selections.all::<Point>(cx);
5047 let mut clipboard_selections = Vec::with_capacity(selections.len());
5048 {
5049 let max_point = buffer.max_point();
5050 let mut is_first = true;
5051 for selection in &mut selections {
5052 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5053 if is_entire_line {
5054 selection.start = Point::new(selection.start.row, 0);
5055 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5056 selection.goal = SelectionGoal::None;
5057 }
5058 if is_first {
5059 is_first = false;
5060 } else {
5061 text += "\n";
5062 }
5063 let mut len = 0;
5064 for chunk in buffer.text_for_range(selection.start..selection.end) {
5065 text.push_str(chunk);
5066 len += chunk.len();
5067 }
5068 clipboard_selections.push(ClipboardSelection {
5069 len,
5070 is_entire_line,
5071 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5072 });
5073 }
5074 }
5075
5076 self.transact(cx, |this, cx| {
5077 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5078 s.select(selections);
5079 });
5080 this.insert("", cx);
5081 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5082 });
5083 }
5084
5085 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5086 let selections = self.selections.all::<Point>(cx);
5087 let buffer = self.buffer.read(cx).read(cx);
5088 let mut text = String::new();
5089
5090 let mut clipboard_selections = Vec::with_capacity(selections.len());
5091 {
5092 let max_point = buffer.max_point();
5093 let mut is_first = true;
5094 for selection in selections.iter() {
5095 let mut start = selection.start;
5096 let mut end = selection.end;
5097 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5098 if is_entire_line {
5099 start = Point::new(start.row, 0);
5100 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5101 }
5102 if is_first {
5103 is_first = false;
5104 } else {
5105 text += "\n";
5106 }
5107 let mut len = 0;
5108 for chunk in buffer.text_for_range(start..end) {
5109 text.push_str(chunk);
5110 len += chunk.len();
5111 }
5112 clipboard_selections.push(ClipboardSelection {
5113 len,
5114 is_entire_line,
5115 first_line_indent: buffer.indent_size_for_line(start.row).len,
5116 });
5117 }
5118 }
5119
5120 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5121 }
5122
5123 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5124 self.transact(cx, |this, cx| {
5125 if let Some(item) = cx.read_from_clipboard() {
5126 let clipboard_text = Cow::Borrowed(item.text());
5127 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5128 let old_selections = this.selections.all::<usize>(cx);
5129 let all_selections_were_entire_line =
5130 clipboard_selections.iter().all(|s| s.is_entire_line);
5131 let first_selection_indent_column =
5132 clipboard_selections.first().map(|s| s.first_line_indent);
5133 if clipboard_selections.len() != old_selections.len() {
5134 clipboard_selections.drain(..);
5135 }
5136
5137 this.buffer.update(cx, |buffer, cx| {
5138 let snapshot = buffer.read(cx);
5139 let mut start_offset = 0;
5140 let mut edits = Vec::new();
5141 let mut original_indent_columns = Vec::new();
5142 let line_mode = this.selections.line_mode;
5143 for (ix, selection) in old_selections.iter().enumerate() {
5144 let to_insert;
5145 let entire_line;
5146 let original_indent_column;
5147 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5148 let end_offset = start_offset + clipboard_selection.len;
5149 to_insert = &clipboard_text[start_offset..end_offset];
5150 entire_line = clipboard_selection.is_entire_line;
5151 start_offset = end_offset + 1;
5152 original_indent_column =
5153 Some(clipboard_selection.first_line_indent);
5154 } else {
5155 to_insert = clipboard_text.as_str();
5156 entire_line = all_selections_were_entire_line;
5157 original_indent_column = first_selection_indent_column
5158 }
5159
5160 // If the corresponding selection was empty when this slice of the
5161 // clipboard text was written, then the entire line containing the
5162 // selection was copied. If this selection is also currently empty,
5163 // then paste the line before the current line of the buffer.
5164 let range = if selection.is_empty() && !line_mode && entire_line {
5165 let column = selection.start.to_point(&snapshot).column as usize;
5166 let line_start = selection.start - column;
5167 line_start..line_start
5168 } else {
5169 selection.range()
5170 };
5171
5172 edits.push((range, to_insert));
5173 original_indent_columns.extend(original_indent_column);
5174 }
5175 drop(snapshot);
5176
5177 buffer.edit(
5178 edits,
5179 Some(AutoindentMode::Block {
5180 original_indent_columns,
5181 }),
5182 cx,
5183 );
5184 });
5185
5186 let selections = this.selections.all::<usize>(cx);
5187 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5188 } else {
5189 this.insert(&clipboard_text, cx);
5190 }
5191 }
5192 });
5193 }
5194
5195 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5196 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5197 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5198 self.change_selections(None, cx, |s| {
5199 s.select_anchors(selections.to_vec());
5200 });
5201 }
5202 self.request_autoscroll(Autoscroll::fit(), cx);
5203 self.unmark_text(cx);
5204 self.refresh_copilot_suggestions(true, cx);
5205 cx.emit(Event::Edited);
5206 }
5207 }
5208
5209 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5210 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5211 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5212 {
5213 self.change_selections(None, cx, |s| {
5214 s.select_anchors(selections.to_vec());
5215 });
5216 }
5217 self.request_autoscroll(Autoscroll::fit(), cx);
5218 self.unmark_text(cx);
5219 self.refresh_copilot_suggestions(true, cx);
5220 cx.emit(Event::Edited);
5221 }
5222 }
5223
5224 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5225 self.buffer
5226 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5227 }
5228
5229 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5230 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5231 let line_mode = s.line_mode;
5232 s.move_with(|map, selection| {
5233 let cursor = if selection.is_empty() && !line_mode {
5234 movement::left(map, selection.start)
5235 } else {
5236 selection.start
5237 };
5238 selection.collapse_to(cursor, SelectionGoal::None);
5239 });
5240 })
5241 }
5242
5243 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5244 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5245 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5246 })
5247 }
5248
5249 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5250 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5251 let line_mode = s.line_mode;
5252 s.move_with(|map, selection| {
5253 let cursor = if selection.is_empty() && !line_mode {
5254 movement::right(map, selection.end)
5255 } else {
5256 selection.end
5257 };
5258 selection.collapse_to(cursor, SelectionGoal::None)
5259 });
5260 })
5261 }
5262
5263 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5264 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5265 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5266 })
5267 }
5268
5269 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5270 if self.take_rename(true, cx).is_some() {
5271 return;
5272 }
5273
5274 if matches!(self.mode, EditorMode::SingleLine) {
5275 cx.propagate_action();
5276 return;
5277 }
5278
5279 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5280 let line_mode = s.line_mode;
5281 s.move_with(|map, selection| {
5282 if !selection.is_empty() && !line_mode {
5283 selection.goal = SelectionGoal::None;
5284 }
5285 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
5286 selection.collapse_to(cursor, goal);
5287 });
5288 })
5289 }
5290
5291 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5292 if self.take_rename(true, cx).is_some() {
5293 return;
5294 }
5295
5296 if matches!(self.mode, EditorMode::SingleLine) {
5297 cx.propagate_action();
5298 return;
5299 }
5300
5301 let row_count = if let Some(row_count) = self.visible_line_count() {
5302 row_count as u32 - 1
5303 } else {
5304 return;
5305 };
5306
5307 let autoscroll = if action.center_cursor {
5308 Autoscroll::center()
5309 } else {
5310 Autoscroll::fit()
5311 };
5312
5313 self.change_selections(Some(autoscroll), cx, |s| {
5314 let line_mode = s.line_mode;
5315 s.move_with(|map, selection| {
5316 if !selection.is_empty() && !line_mode {
5317 selection.goal = SelectionGoal::None;
5318 }
5319 let (cursor, goal) =
5320 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5321 selection.collapse_to(cursor, goal);
5322 });
5323 });
5324 }
5325
5326 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5327 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5328 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5329 })
5330 }
5331
5332 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5333 self.take_rename(true, cx);
5334
5335 if self.mode == EditorMode::SingleLine {
5336 cx.propagate_action();
5337 return;
5338 }
5339
5340 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5341 let line_mode = s.line_mode;
5342 s.move_with(|map, selection| {
5343 if !selection.is_empty() && !line_mode {
5344 selection.goal = SelectionGoal::None;
5345 }
5346 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5347 selection.collapse_to(cursor, goal);
5348 });
5349 });
5350 }
5351
5352 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5353 if self.take_rename(true, cx).is_some() {
5354 return;
5355 }
5356
5357 if self
5358 .context_menu
5359 .as_mut()
5360 .map(|menu| menu.select_last(cx))
5361 .unwrap_or(false)
5362 {
5363 return;
5364 }
5365
5366 if matches!(self.mode, EditorMode::SingleLine) {
5367 cx.propagate_action();
5368 return;
5369 }
5370
5371 let row_count = if let Some(row_count) = self.visible_line_count() {
5372 row_count as u32 - 1
5373 } else {
5374 return;
5375 };
5376
5377 let autoscroll = if action.center_cursor {
5378 Autoscroll::center()
5379 } else {
5380 Autoscroll::fit()
5381 };
5382
5383 self.change_selections(Some(autoscroll), cx, |s| {
5384 let line_mode = s.line_mode;
5385 s.move_with(|map, selection| {
5386 if !selection.is_empty() && !line_mode {
5387 selection.goal = SelectionGoal::None;
5388 }
5389 let (cursor, goal) =
5390 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5391 selection.collapse_to(cursor, goal);
5392 });
5393 });
5394 }
5395
5396 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5397 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5398 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5399 });
5400 }
5401
5402 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5403 if let Some(context_menu) = self.context_menu.as_mut() {
5404 context_menu.select_first(cx);
5405 }
5406 }
5407
5408 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5409 if let Some(context_menu) = self.context_menu.as_mut() {
5410 context_menu.select_prev(cx);
5411 }
5412 }
5413
5414 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5415 if let Some(context_menu) = self.context_menu.as_mut() {
5416 context_menu.select_next(cx);
5417 }
5418 }
5419
5420 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5421 if let Some(context_menu) = self.context_menu.as_mut() {
5422 context_menu.select_last(cx);
5423 }
5424 }
5425
5426 pub fn move_to_previous_word_start(
5427 &mut self,
5428 _: &MoveToPreviousWordStart,
5429 cx: &mut ViewContext<Self>,
5430 ) {
5431 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5432 s.move_cursors_with(|map, head, _| {
5433 (
5434 movement::previous_word_start(map, head),
5435 SelectionGoal::None,
5436 )
5437 });
5438 })
5439 }
5440
5441 pub fn move_to_previous_subword_start(
5442 &mut self,
5443 _: &MoveToPreviousSubwordStart,
5444 cx: &mut ViewContext<Self>,
5445 ) {
5446 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5447 s.move_cursors_with(|map, head, _| {
5448 (
5449 movement::previous_subword_start(map, head),
5450 SelectionGoal::None,
5451 )
5452 });
5453 })
5454 }
5455
5456 pub fn select_to_previous_word_start(
5457 &mut self,
5458 _: &SelectToPreviousWordStart,
5459 cx: &mut ViewContext<Self>,
5460 ) {
5461 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5462 s.move_heads_with(|map, head, _| {
5463 (
5464 movement::previous_word_start(map, head),
5465 SelectionGoal::None,
5466 )
5467 });
5468 })
5469 }
5470
5471 pub fn select_to_previous_subword_start(
5472 &mut self,
5473 _: &SelectToPreviousSubwordStart,
5474 cx: &mut ViewContext<Self>,
5475 ) {
5476 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5477 s.move_heads_with(|map, head, _| {
5478 (
5479 movement::previous_subword_start(map, head),
5480 SelectionGoal::None,
5481 )
5482 });
5483 })
5484 }
5485
5486 pub fn delete_to_previous_word_start(
5487 &mut self,
5488 _: &DeleteToPreviousWordStart,
5489 cx: &mut ViewContext<Self>,
5490 ) {
5491 self.transact(cx, |this, cx| {
5492 this.select_autoclose_pair(cx);
5493 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5494 let line_mode = s.line_mode;
5495 s.move_with(|map, selection| {
5496 if selection.is_empty() && !line_mode {
5497 let cursor = movement::previous_word_start(map, selection.head());
5498 selection.set_head(cursor, SelectionGoal::None);
5499 }
5500 });
5501 });
5502 this.insert("", cx);
5503 });
5504 }
5505
5506 pub fn delete_to_previous_subword_start(
5507 &mut self,
5508 _: &DeleteToPreviousSubwordStart,
5509 cx: &mut ViewContext<Self>,
5510 ) {
5511 self.transact(cx, |this, cx| {
5512 this.select_autoclose_pair(cx);
5513 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5514 let line_mode = s.line_mode;
5515 s.move_with(|map, selection| {
5516 if selection.is_empty() && !line_mode {
5517 let cursor = movement::previous_subword_start(map, selection.head());
5518 selection.set_head(cursor, SelectionGoal::None);
5519 }
5520 });
5521 });
5522 this.insert("", cx);
5523 });
5524 }
5525
5526 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5527 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5528 s.move_cursors_with(|map, head, _| {
5529 (movement::next_word_end(map, head), SelectionGoal::None)
5530 });
5531 })
5532 }
5533
5534 pub fn move_to_next_subword_end(
5535 &mut self,
5536 _: &MoveToNextSubwordEnd,
5537 cx: &mut ViewContext<Self>,
5538 ) {
5539 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5540 s.move_cursors_with(|map, head, _| {
5541 (movement::next_subword_end(map, head), SelectionGoal::None)
5542 });
5543 })
5544 }
5545
5546 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5547 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5548 s.move_heads_with(|map, head, _| {
5549 (movement::next_word_end(map, head), SelectionGoal::None)
5550 });
5551 })
5552 }
5553
5554 pub fn select_to_next_subword_end(
5555 &mut self,
5556 _: &SelectToNextSubwordEnd,
5557 cx: &mut ViewContext<Self>,
5558 ) {
5559 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5560 s.move_heads_with(|map, head, _| {
5561 (movement::next_subword_end(map, head), SelectionGoal::None)
5562 });
5563 })
5564 }
5565
5566 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5567 self.transact(cx, |this, cx| {
5568 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5569 let line_mode = s.line_mode;
5570 s.move_with(|map, selection| {
5571 if selection.is_empty() && !line_mode {
5572 let cursor = movement::next_word_end(map, selection.head());
5573 selection.set_head(cursor, SelectionGoal::None);
5574 }
5575 });
5576 });
5577 this.insert("", cx);
5578 });
5579 }
5580
5581 pub fn delete_to_next_subword_end(
5582 &mut self,
5583 _: &DeleteToNextSubwordEnd,
5584 cx: &mut ViewContext<Self>,
5585 ) {
5586 self.transact(cx, |this, cx| {
5587 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5588 s.move_with(|map, selection| {
5589 if selection.is_empty() {
5590 let cursor = movement::next_subword_end(map, selection.head());
5591 selection.set_head(cursor, SelectionGoal::None);
5592 }
5593 });
5594 });
5595 this.insert("", cx);
5596 });
5597 }
5598
5599 pub fn move_to_beginning_of_line(
5600 &mut self,
5601 _: &MoveToBeginningOfLine,
5602 cx: &mut ViewContext<Self>,
5603 ) {
5604 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5605 s.move_cursors_with(|map, head, _| {
5606 (
5607 movement::indented_line_beginning(map, head, true),
5608 SelectionGoal::None,
5609 )
5610 });
5611 })
5612 }
5613
5614 pub fn select_to_beginning_of_line(
5615 &mut self,
5616 action: &SelectToBeginningOfLine,
5617 cx: &mut ViewContext<Self>,
5618 ) {
5619 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5620 s.move_heads_with(|map, head, _| {
5621 (
5622 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5623 SelectionGoal::None,
5624 )
5625 });
5626 });
5627 }
5628
5629 pub fn delete_to_beginning_of_line(
5630 &mut self,
5631 _: &DeleteToBeginningOfLine,
5632 cx: &mut ViewContext<Self>,
5633 ) {
5634 self.transact(cx, |this, cx| {
5635 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5636 s.move_with(|_, selection| {
5637 selection.reversed = true;
5638 });
5639 });
5640
5641 this.select_to_beginning_of_line(
5642 &SelectToBeginningOfLine {
5643 stop_at_soft_wraps: false,
5644 },
5645 cx,
5646 );
5647 this.backspace(&Backspace, cx);
5648 });
5649 }
5650
5651 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5652 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5653 s.move_cursors_with(|map, head, _| {
5654 (movement::line_end(map, head, true), SelectionGoal::None)
5655 });
5656 })
5657 }
5658
5659 pub fn select_to_end_of_line(
5660 &mut self,
5661 action: &SelectToEndOfLine,
5662 cx: &mut ViewContext<Self>,
5663 ) {
5664 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5665 s.move_heads_with(|map, head, _| {
5666 (
5667 movement::line_end(map, head, action.stop_at_soft_wraps),
5668 SelectionGoal::None,
5669 )
5670 });
5671 })
5672 }
5673
5674 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5675 self.transact(cx, |this, cx| {
5676 this.select_to_end_of_line(
5677 &SelectToEndOfLine {
5678 stop_at_soft_wraps: false,
5679 },
5680 cx,
5681 );
5682 this.delete(&Delete, cx);
5683 });
5684 }
5685
5686 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5687 self.transact(cx, |this, cx| {
5688 this.select_to_end_of_line(
5689 &SelectToEndOfLine {
5690 stop_at_soft_wraps: false,
5691 },
5692 cx,
5693 );
5694 this.cut(&Cut, cx);
5695 });
5696 }
5697
5698 pub fn move_to_start_of_paragraph(
5699 &mut self,
5700 _: &MoveToStartOfParagraph,
5701 cx: &mut ViewContext<Self>,
5702 ) {
5703 if matches!(self.mode, EditorMode::SingleLine) {
5704 cx.propagate_action();
5705 return;
5706 }
5707
5708 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5709 s.move_with(|map, selection| {
5710 selection.collapse_to(
5711 movement::start_of_paragraph(map, selection.head(), 1),
5712 SelectionGoal::None,
5713 )
5714 });
5715 })
5716 }
5717
5718 pub fn move_to_end_of_paragraph(
5719 &mut self,
5720 _: &MoveToEndOfParagraph,
5721 cx: &mut ViewContext<Self>,
5722 ) {
5723 if matches!(self.mode, EditorMode::SingleLine) {
5724 cx.propagate_action();
5725 return;
5726 }
5727
5728 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5729 s.move_with(|map, selection| {
5730 selection.collapse_to(
5731 movement::end_of_paragraph(map, selection.head(), 1),
5732 SelectionGoal::None,
5733 )
5734 });
5735 })
5736 }
5737
5738 pub fn select_to_start_of_paragraph(
5739 &mut self,
5740 _: &SelectToStartOfParagraph,
5741 cx: &mut ViewContext<Self>,
5742 ) {
5743 if matches!(self.mode, EditorMode::SingleLine) {
5744 cx.propagate_action();
5745 return;
5746 }
5747
5748 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5749 s.move_heads_with(|map, head, _| {
5750 (
5751 movement::start_of_paragraph(map, head, 1),
5752 SelectionGoal::None,
5753 )
5754 });
5755 })
5756 }
5757
5758 pub fn select_to_end_of_paragraph(
5759 &mut self,
5760 _: &SelectToEndOfParagraph,
5761 cx: &mut ViewContext<Self>,
5762 ) {
5763 if matches!(self.mode, EditorMode::SingleLine) {
5764 cx.propagate_action();
5765 return;
5766 }
5767
5768 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5769 s.move_heads_with(|map, head, _| {
5770 (
5771 movement::end_of_paragraph(map, head, 1),
5772 SelectionGoal::None,
5773 )
5774 });
5775 })
5776 }
5777
5778 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5779 if matches!(self.mode, EditorMode::SingleLine) {
5780 cx.propagate_action();
5781 return;
5782 }
5783
5784 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5785 s.select_ranges(vec![0..0]);
5786 });
5787 }
5788
5789 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5790 let mut selection = self.selections.last::<Point>(cx);
5791 selection.set_head(Point::zero(), SelectionGoal::None);
5792
5793 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5794 s.select(vec![selection]);
5795 });
5796 }
5797
5798 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5799 if matches!(self.mode, EditorMode::SingleLine) {
5800 cx.propagate_action();
5801 return;
5802 }
5803
5804 let cursor = self.buffer.read(cx).read(cx).len();
5805 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5806 s.select_ranges(vec![cursor..cursor])
5807 });
5808 }
5809
5810 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5811 self.nav_history = nav_history;
5812 }
5813
5814 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5815 self.nav_history.as_ref()
5816 }
5817
5818 fn push_to_nav_history(
5819 &mut self,
5820 cursor_anchor: Anchor,
5821 new_position: Option<Point>,
5822 cx: &mut ViewContext<Self>,
5823 ) {
5824 if let Some(nav_history) = self.nav_history.as_mut() {
5825 let buffer = self.buffer.read(cx).read(cx);
5826 let cursor_position = cursor_anchor.to_point(&buffer);
5827 let scroll_state = self.scroll_manager.anchor();
5828 let scroll_top_row = scroll_state.top_row(&buffer);
5829 drop(buffer);
5830
5831 if let Some(new_position) = new_position {
5832 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5833 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5834 return;
5835 }
5836 }
5837
5838 nav_history.push(
5839 Some(NavigationData {
5840 cursor_anchor,
5841 cursor_position,
5842 scroll_anchor: scroll_state,
5843 scroll_top_row,
5844 }),
5845 cx,
5846 );
5847 }
5848 }
5849
5850 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5851 let buffer = self.buffer.read(cx).snapshot(cx);
5852 let mut selection = self.selections.first::<usize>(cx);
5853 selection.set_head(buffer.len(), SelectionGoal::None);
5854 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5855 s.select(vec![selection]);
5856 });
5857 }
5858
5859 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5860 let end = self.buffer.read(cx).read(cx).len();
5861 self.change_selections(None, cx, |s| {
5862 s.select_ranges(vec![0..end]);
5863 });
5864 }
5865
5866 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5867 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5868 let mut selections = self.selections.all::<Point>(cx);
5869 let max_point = display_map.buffer_snapshot.max_point();
5870 for selection in &mut selections {
5871 let rows = selection.spanned_rows(true, &display_map);
5872 selection.start = Point::new(rows.start, 0);
5873 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5874 selection.reversed = false;
5875 }
5876 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5877 s.select(selections);
5878 });
5879 }
5880
5881 pub fn split_selection_into_lines(
5882 &mut self,
5883 _: &SplitSelectionIntoLines,
5884 cx: &mut ViewContext<Self>,
5885 ) {
5886 let mut to_unfold = Vec::new();
5887 let mut new_selection_ranges = Vec::new();
5888 {
5889 let selections = self.selections.all::<Point>(cx);
5890 let buffer = self.buffer.read(cx).read(cx);
5891 for selection in selections {
5892 for row in selection.start.row..selection.end.row {
5893 let cursor = Point::new(row, buffer.line_len(row));
5894 new_selection_ranges.push(cursor..cursor);
5895 }
5896 new_selection_ranges.push(selection.end..selection.end);
5897 to_unfold.push(selection.start..selection.end);
5898 }
5899 }
5900 self.unfold_ranges(to_unfold, true, true, cx);
5901 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5902 s.select_ranges(new_selection_ranges);
5903 });
5904 }
5905
5906 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5907 self.add_selection(true, cx);
5908 }
5909
5910 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5911 self.add_selection(false, cx);
5912 }
5913
5914 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5915 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5916 let mut selections = self.selections.all::<Point>(cx);
5917 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5918 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5919 let range = oldest_selection.display_range(&display_map).sorted();
5920 let columns = cmp::min(range.start.column(), range.end.column())
5921 ..cmp::max(range.start.column(), range.end.column());
5922
5923 selections.clear();
5924 let mut stack = Vec::new();
5925 for row in range.start.row()..=range.end.row() {
5926 if let Some(selection) = self.selections.build_columnar_selection(
5927 &display_map,
5928 row,
5929 &columns,
5930 oldest_selection.reversed,
5931 ) {
5932 stack.push(selection.id);
5933 selections.push(selection);
5934 }
5935 }
5936
5937 if above {
5938 stack.reverse();
5939 }
5940
5941 AddSelectionsState { above, stack }
5942 });
5943
5944 let last_added_selection = *state.stack.last().unwrap();
5945 let mut new_selections = Vec::new();
5946 if above == state.above {
5947 let end_row = if above {
5948 0
5949 } else {
5950 display_map.max_point().row()
5951 };
5952
5953 'outer: for selection in selections {
5954 if selection.id == last_added_selection {
5955 let range = selection.display_range(&display_map).sorted();
5956 debug_assert_eq!(range.start.row(), range.end.row());
5957 let mut row = range.start.row();
5958 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5959 {
5960 start..end
5961 } else {
5962 cmp::min(range.start.column(), range.end.column())
5963 ..cmp::max(range.start.column(), range.end.column())
5964 };
5965
5966 while row != end_row {
5967 if above {
5968 row -= 1;
5969 } else {
5970 row += 1;
5971 }
5972
5973 if let Some(new_selection) = self.selections.build_columnar_selection(
5974 &display_map,
5975 row,
5976 &columns,
5977 selection.reversed,
5978 ) {
5979 state.stack.push(new_selection.id);
5980 if above {
5981 new_selections.push(new_selection);
5982 new_selections.push(selection);
5983 } else {
5984 new_selections.push(selection);
5985 new_selections.push(new_selection);
5986 }
5987
5988 continue 'outer;
5989 }
5990 }
5991 }
5992
5993 new_selections.push(selection);
5994 }
5995 } else {
5996 new_selections = selections;
5997 new_selections.retain(|s| s.id != last_added_selection);
5998 state.stack.pop();
5999 }
6000
6001 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6002 s.select(new_selections);
6003 });
6004 if state.stack.len() > 1 {
6005 self.add_selections_state = Some(state);
6006 }
6007 }
6008
6009 pub fn select_next_match_internal(
6010 &mut self,
6011 display_map: &DisplaySnapshot,
6012 replace_newest: bool,
6013 autoscroll: Option<Autoscroll>,
6014 cx: &mut ViewContext<Self>,
6015 ) -> Result<()> {
6016 fn select_next_match_ranges(
6017 this: &mut Editor,
6018 range: Range<usize>,
6019 replace_newest: bool,
6020 auto_scroll: Option<Autoscroll>,
6021 cx: &mut ViewContext<Editor>,
6022 ) {
6023 this.unfold_ranges([range.clone()], false, true, cx);
6024 this.change_selections(auto_scroll, cx, |s| {
6025 if replace_newest {
6026 s.delete(s.newest_anchor().id);
6027 }
6028 s.insert_range(range.clone());
6029 });
6030 }
6031
6032 let buffer = &display_map.buffer_snapshot;
6033 let mut selections = self.selections.all::<usize>(cx);
6034 if let Some(mut select_next_state) = self.select_next_state.take() {
6035 let query = &select_next_state.query;
6036 if !select_next_state.done {
6037 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6038 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6039 let mut next_selected_range = None;
6040
6041 let bytes_after_last_selection =
6042 buffer.bytes_in_range(last_selection.end..buffer.len());
6043 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6044 let query_matches = query
6045 .stream_find_iter(bytes_after_last_selection)
6046 .map(|result| (last_selection.end, result))
6047 .chain(
6048 query
6049 .stream_find_iter(bytes_before_first_selection)
6050 .map(|result| (0, result)),
6051 );
6052
6053 for (start_offset, query_match) in query_matches {
6054 let query_match = query_match.unwrap(); // can only fail due to I/O
6055 let offset_range =
6056 start_offset + query_match.start()..start_offset + query_match.end();
6057 let display_range = offset_range.start.to_display_point(&display_map)
6058 ..offset_range.end.to_display_point(&display_map);
6059
6060 if !select_next_state.wordwise
6061 || (!movement::is_inside_word(&display_map, display_range.start)
6062 && !movement::is_inside_word(&display_map, display_range.end))
6063 {
6064 if selections
6065 .iter()
6066 .find(|selection| selection.equals(&offset_range))
6067 .is_none()
6068 {
6069 next_selected_range = Some(offset_range);
6070 break;
6071 }
6072 }
6073 }
6074
6075 if let Some(next_selected_range) = next_selected_range {
6076 select_next_match_ranges(
6077 self,
6078 next_selected_range,
6079 replace_newest,
6080 autoscroll,
6081 cx,
6082 );
6083 } else {
6084 select_next_state.done = true;
6085 }
6086 }
6087
6088 self.select_next_state = Some(select_next_state);
6089 } else if selections.len() == 1 {
6090 let selection = selections.last_mut().unwrap();
6091 if selection.start == selection.end {
6092 let word_range = movement::surrounding_word(
6093 &display_map,
6094 selection.start.to_display_point(&display_map),
6095 );
6096 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6097 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6098 selection.goal = SelectionGoal::None;
6099 selection.reversed = false;
6100
6101 let query = buffer
6102 .text_for_range(selection.start..selection.end)
6103 .collect::<String>();
6104
6105 let is_empty = query.is_empty();
6106 let select_state = SelectNextState {
6107 query: AhoCorasick::new(&[query])?,
6108 wordwise: true,
6109 done: is_empty,
6110 };
6111 select_next_match_ranges(
6112 self,
6113 selection.start..selection.end,
6114 replace_newest,
6115 autoscroll,
6116 cx,
6117 );
6118 self.select_next_state = Some(select_state);
6119 } else {
6120 let query = buffer
6121 .text_for_range(selection.start..selection.end)
6122 .collect::<String>();
6123 self.select_next_state = Some(SelectNextState {
6124 query: AhoCorasick::new(&[query])?,
6125 wordwise: false,
6126 done: false,
6127 });
6128 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6129 }
6130 }
6131 Ok(())
6132 }
6133
6134 pub fn select_all_matches(
6135 &mut self,
6136 action: &SelectAllMatches,
6137 cx: &mut ViewContext<Self>,
6138 ) -> Result<()> {
6139 self.push_to_selection_history();
6140 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6141
6142 loop {
6143 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6144
6145 if self
6146 .select_next_state
6147 .as_ref()
6148 .map(|selection_state| selection_state.done)
6149 .unwrap_or(true)
6150 {
6151 break;
6152 }
6153 }
6154
6155 Ok(())
6156 }
6157
6158 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6159 self.push_to_selection_history();
6160 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6161 self.select_next_match_internal(
6162 &display_map,
6163 action.replace_newest,
6164 Some(Autoscroll::newest()),
6165 cx,
6166 )?;
6167 Ok(())
6168 }
6169
6170 pub fn select_previous(
6171 &mut self,
6172 action: &SelectPrevious,
6173 cx: &mut ViewContext<Self>,
6174 ) -> Result<()> {
6175 self.push_to_selection_history();
6176 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6177 let buffer = &display_map.buffer_snapshot;
6178 let mut selections = self.selections.all::<usize>(cx);
6179 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6180 let query = &select_prev_state.query;
6181 if !select_prev_state.done {
6182 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6183 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6184 let mut next_selected_range = None;
6185 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6186 let bytes_before_last_selection =
6187 buffer.reversed_bytes_in_range(0..last_selection.start);
6188 let bytes_after_first_selection =
6189 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6190 let query_matches = query
6191 .stream_find_iter(bytes_before_last_selection)
6192 .map(|result| (last_selection.start, result))
6193 .chain(
6194 query
6195 .stream_find_iter(bytes_after_first_selection)
6196 .map(|result| (buffer.len(), result)),
6197 );
6198 for (end_offset, query_match) in query_matches {
6199 let query_match = query_match.unwrap(); // can only fail due to I/O
6200 let offset_range =
6201 end_offset - query_match.end()..end_offset - query_match.start();
6202 let display_range = offset_range.start.to_display_point(&display_map)
6203 ..offset_range.end.to_display_point(&display_map);
6204
6205 if !select_prev_state.wordwise
6206 || (!movement::is_inside_word(&display_map, display_range.start)
6207 && !movement::is_inside_word(&display_map, display_range.end))
6208 {
6209 next_selected_range = Some(offset_range);
6210 break;
6211 }
6212 }
6213
6214 if let Some(next_selected_range) = next_selected_range {
6215 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6216 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6217 if action.replace_newest {
6218 s.delete(s.newest_anchor().id);
6219 }
6220 s.insert_range(next_selected_range);
6221 });
6222 } else {
6223 select_prev_state.done = true;
6224 }
6225 }
6226
6227 self.select_prev_state = Some(select_prev_state);
6228 } else if selections.len() == 1 {
6229 let selection = selections.last_mut().unwrap();
6230 if selection.start == selection.end {
6231 let word_range = movement::surrounding_word(
6232 &display_map,
6233 selection.start.to_display_point(&display_map),
6234 );
6235 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6236 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6237 selection.goal = SelectionGoal::None;
6238 selection.reversed = false;
6239
6240 let query = buffer
6241 .text_for_range(selection.start..selection.end)
6242 .collect::<String>();
6243 let query = query.chars().rev().collect::<String>();
6244 let select_state = SelectNextState {
6245 query: AhoCorasick::new(&[query])?,
6246 wordwise: true,
6247 done: false,
6248 };
6249 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6250 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6251 s.select(selections);
6252 });
6253 self.select_prev_state = Some(select_state);
6254 } else {
6255 let query = buffer
6256 .text_for_range(selection.start..selection.end)
6257 .collect::<String>();
6258 let query = query.chars().rev().collect::<String>();
6259 self.select_prev_state = Some(SelectNextState {
6260 query: AhoCorasick::new(&[query])?,
6261 wordwise: false,
6262 done: false,
6263 });
6264 self.select_previous(action, cx)?;
6265 }
6266 }
6267 Ok(())
6268 }
6269
6270 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6271 self.transact(cx, |this, cx| {
6272 let mut selections = this.selections.all::<Point>(cx);
6273 let mut edits = Vec::new();
6274 let mut selection_edit_ranges = Vec::new();
6275 let mut last_toggled_row = None;
6276 let snapshot = this.buffer.read(cx).read(cx);
6277 let empty_str: Arc<str> = "".into();
6278 let mut suffixes_inserted = Vec::new();
6279
6280 fn comment_prefix_range(
6281 snapshot: &MultiBufferSnapshot,
6282 row: u32,
6283 comment_prefix: &str,
6284 comment_prefix_whitespace: &str,
6285 ) -> Range<Point> {
6286 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6287
6288 let mut line_bytes = snapshot
6289 .bytes_in_range(start..snapshot.max_point())
6290 .flatten()
6291 .copied();
6292
6293 // If this line currently begins with the line comment prefix, then record
6294 // the range containing the prefix.
6295 if line_bytes
6296 .by_ref()
6297 .take(comment_prefix.len())
6298 .eq(comment_prefix.bytes())
6299 {
6300 // Include any whitespace that matches the comment prefix.
6301 let matching_whitespace_len = line_bytes
6302 .zip(comment_prefix_whitespace.bytes())
6303 .take_while(|(a, b)| a == b)
6304 .count() as u32;
6305 let end = Point::new(
6306 start.row,
6307 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6308 );
6309 start..end
6310 } else {
6311 start..start
6312 }
6313 }
6314
6315 fn comment_suffix_range(
6316 snapshot: &MultiBufferSnapshot,
6317 row: u32,
6318 comment_suffix: &str,
6319 comment_suffix_has_leading_space: bool,
6320 ) -> Range<Point> {
6321 let end = Point::new(row, snapshot.line_len(row));
6322 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6323
6324 let mut line_end_bytes = snapshot
6325 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6326 .flatten()
6327 .copied();
6328
6329 let leading_space_len = if suffix_start_column > 0
6330 && line_end_bytes.next() == Some(b' ')
6331 && comment_suffix_has_leading_space
6332 {
6333 1
6334 } else {
6335 0
6336 };
6337
6338 // If this line currently begins with the line comment prefix, then record
6339 // the range containing the prefix.
6340 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6341 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6342 start..end
6343 } else {
6344 end..end
6345 }
6346 }
6347
6348 // TODO: Handle selections that cross excerpts
6349 for selection in &mut selections {
6350 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6351 let language = if let Some(language) =
6352 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6353 {
6354 language
6355 } else {
6356 continue;
6357 };
6358
6359 selection_edit_ranges.clear();
6360
6361 // If multiple selections contain a given row, avoid processing that
6362 // row more than once.
6363 let mut start_row = selection.start.row;
6364 if last_toggled_row == Some(start_row) {
6365 start_row += 1;
6366 }
6367 let end_row =
6368 if selection.end.row > selection.start.row && selection.end.column == 0 {
6369 selection.end.row - 1
6370 } else {
6371 selection.end.row
6372 };
6373 last_toggled_row = Some(end_row);
6374
6375 if start_row > end_row {
6376 continue;
6377 }
6378
6379 // If the language has line comments, toggle those.
6380 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6381 // Split the comment prefix's trailing whitespace into a separate string,
6382 // as that portion won't be used for detecting if a line is a comment.
6383 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6384 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6385 let mut all_selection_lines_are_comments = true;
6386
6387 for row in start_row..=end_row {
6388 if snapshot.is_line_blank(row) && start_row < end_row {
6389 continue;
6390 }
6391
6392 let prefix_range = comment_prefix_range(
6393 snapshot.deref(),
6394 row,
6395 comment_prefix,
6396 comment_prefix_whitespace,
6397 );
6398 if prefix_range.is_empty() {
6399 all_selection_lines_are_comments = false;
6400 }
6401 selection_edit_ranges.push(prefix_range);
6402 }
6403
6404 if all_selection_lines_are_comments {
6405 edits.extend(
6406 selection_edit_ranges
6407 .iter()
6408 .cloned()
6409 .map(|range| (range, empty_str.clone())),
6410 );
6411 } else {
6412 let min_column = selection_edit_ranges
6413 .iter()
6414 .map(|r| r.start.column)
6415 .min()
6416 .unwrap_or(0);
6417 edits.extend(selection_edit_ranges.iter().map(|range| {
6418 let position = Point::new(range.start.row, min_column);
6419 (position..position, full_comment_prefix.clone())
6420 }));
6421 }
6422 } else if let Some((full_comment_prefix, comment_suffix)) =
6423 language.block_comment_delimiters()
6424 {
6425 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6426 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6427 let prefix_range = comment_prefix_range(
6428 snapshot.deref(),
6429 start_row,
6430 comment_prefix,
6431 comment_prefix_whitespace,
6432 );
6433 let suffix_range = comment_suffix_range(
6434 snapshot.deref(),
6435 end_row,
6436 comment_suffix.trim_start_matches(' '),
6437 comment_suffix.starts_with(' '),
6438 );
6439
6440 if prefix_range.is_empty() || suffix_range.is_empty() {
6441 edits.push((
6442 prefix_range.start..prefix_range.start,
6443 full_comment_prefix.clone(),
6444 ));
6445 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6446 suffixes_inserted.push((end_row, comment_suffix.len()));
6447 } else {
6448 edits.push((prefix_range, empty_str.clone()));
6449 edits.push((suffix_range, empty_str.clone()));
6450 }
6451 } else {
6452 continue;
6453 }
6454 }
6455
6456 drop(snapshot);
6457 this.buffer.update(cx, |buffer, cx| {
6458 buffer.edit(edits, None, cx);
6459 });
6460
6461 // Adjust selections so that they end before any comment suffixes that
6462 // were inserted.
6463 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6464 let mut selections = this.selections.all::<Point>(cx);
6465 let snapshot = this.buffer.read(cx).read(cx);
6466 for selection in &mut selections {
6467 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6468 match row.cmp(&selection.end.row) {
6469 Ordering::Less => {
6470 suffixes_inserted.next();
6471 continue;
6472 }
6473 Ordering::Greater => break,
6474 Ordering::Equal => {
6475 if selection.end.column == snapshot.line_len(row) {
6476 if selection.is_empty() {
6477 selection.start.column -= suffix_len as u32;
6478 }
6479 selection.end.column -= suffix_len as u32;
6480 }
6481 break;
6482 }
6483 }
6484 }
6485 }
6486
6487 drop(snapshot);
6488 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6489
6490 let selections = this.selections.all::<Point>(cx);
6491 let selections_on_single_row = selections.windows(2).all(|selections| {
6492 selections[0].start.row == selections[1].start.row
6493 && selections[0].end.row == selections[1].end.row
6494 && selections[0].start.row == selections[0].end.row
6495 });
6496 let selections_selecting = selections
6497 .iter()
6498 .any(|selection| selection.start != selection.end);
6499 let advance_downwards = action.advance_downwards
6500 && selections_on_single_row
6501 && !selections_selecting
6502 && this.mode != EditorMode::SingleLine;
6503
6504 if advance_downwards {
6505 let snapshot = this.buffer.read(cx).snapshot(cx);
6506
6507 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6508 s.move_cursors_with(|display_snapshot, display_point, _| {
6509 let mut point = display_point.to_point(display_snapshot);
6510 point.row += 1;
6511 point = snapshot.clip_point(point, Bias::Left);
6512 let display_point = point.to_display_point(display_snapshot);
6513 (display_point, SelectionGoal::Column(display_point.column()))
6514 })
6515 });
6516 }
6517 });
6518 }
6519
6520 pub fn select_larger_syntax_node(
6521 &mut self,
6522 _: &SelectLargerSyntaxNode,
6523 cx: &mut ViewContext<Self>,
6524 ) {
6525 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6526 let buffer = self.buffer.read(cx).snapshot(cx);
6527 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6528
6529 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6530 let mut selected_larger_node = false;
6531 let new_selections = old_selections
6532 .iter()
6533 .map(|selection| {
6534 let old_range = selection.start..selection.end;
6535 let mut new_range = old_range.clone();
6536 while let Some(containing_range) =
6537 buffer.range_for_syntax_ancestor(new_range.clone())
6538 {
6539 new_range = containing_range;
6540 if !display_map.intersects_fold(new_range.start)
6541 && !display_map.intersects_fold(new_range.end)
6542 {
6543 break;
6544 }
6545 }
6546
6547 selected_larger_node |= new_range != old_range;
6548 Selection {
6549 id: selection.id,
6550 start: new_range.start,
6551 end: new_range.end,
6552 goal: SelectionGoal::None,
6553 reversed: selection.reversed,
6554 }
6555 })
6556 .collect::<Vec<_>>();
6557
6558 if selected_larger_node {
6559 stack.push(old_selections);
6560 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6561 s.select(new_selections);
6562 });
6563 }
6564 self.select_larger_syntax_node_stack = stack;
6565 }
6566
6567 pub fn select_smaller_syntax_node(
6568 &mut self,
6569 _: &SelectSmallerSyntaxNode,
6570 cx: &mut ViewContext<Self>,
6571 ) {
6572 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6573 if let Some(selections) = stack.pop() {
6574 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6575 s.select(selections.to_vec());
6576 });
6577 }
6578 self.select_larger_syntax_node_stack = stack;
6579 }
6580
6581 pub fn move_to_enclosing_bracket(
6582 &mut self,
6583 _: &MoveToEnclosingBracket,
6584 cx: &mut ViewContext<Self>,
6585 ) {
6586 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6587 s.move_offsets_with(|snapshot, selection| {
6588 let Some(enclosing_bracket_ranges) =
6589 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6590 else {
6591 return;
6592 };
6593
6594 let mut best_length = usize::MAX;
6595 let mut best_inside = false;
6596 let mut best_in_bracket_range = false;
6597 let mut best_destination = None;
6598 for (open, close) in enclosing_bracket_ranges {
6599 let close = close.to_inclusive();
6600 let length = close.end() - open.start;
6601 let inside = selection.start >= open.end && selection.end <= *close.start();
6602 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6603 || close.contains(&selection.head());
6604
6605 // If best is next to a bracket and current isn't, skip
6606 if !in_bracket_range && best_in_bracket_range {
6607 continue;
6608 }
6609
6610 // Prefer smaller lengths unless best is inside and current isn't
6611 if length > best_length && (best_inside || !inside) {
6612 continue;
6613 }
6614
6615 best_length = length;
6616 best_inside = inside;
6617 best_in_bracket_range = in_bracket_range;
6618 best_destination = Some(
6619 if close.contains(&selection.start) && close.contains(&selection.end) {
6620 if inside {
6621 open.end
6622 } else {
6623 open.start
6624 }
6625 } else {
6626 if inside {
6627 *close.start()
6628 } else {
6629 *close.end()
6630 }
6631 },
6632 );
6633 }
6634
6635 if let Some(destination) = best_destination {
6636 selection.collapse_to(destination, SelectionGoal::None);
6637 }
6638 })
6639 });
6640 }
6641
6642 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6643 self.end_selection(cx);
6644 self.selection_history.mode = SelectionHistoryMode::Undoing;
6645 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6646 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6647 self.select_next_state = entry.select_next_state;
6648 self.select_prev_state = entry.select_prev_state;
6649 self.add_selections_state = entry.add_selections_state;
6650 self.request_autoscroll(Autoscroll::newest(), cx);
6651 }
6652 self.selection_history.mode = SelectionHistoryMode::Normal;
6653 }
6654
6655 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6656 self.end_selection(cx);
6657 self.selection_history.mode = SelectionHistoryMode::Redoing;
6658 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6659 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6660 self.select_next_state = entry.select_next_state;
6661 self.select_prev_state = entry.select_prev_state;
6662 self.add_selections_state = entry.add_selections_state;
6663 self.request_autoscroll(Autoscroll::newest(), cx);
6664 }
6665 self.selection_history.mode = SelectionHistoryMode::Normal;
6666 }
6667
6668 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6669 self.go_to_diagnostic_impl(Direction::Next, cx)
6670 }
6671
6672 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6673 self.go_to_diagnostic_impl(Direction::Prev, cx)
6674 }
6675
6676 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6677 let buffer = self.buffer.read(cx).snapshot(cx);
6678 let selection = self.selections.newest::<usize>(cx);
6679
6680 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6681 if direction == Direction::Next {
6682 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6683 let (group_id, jump_to) = popover.activation_info();
6684 if self.activate_diagnostics(group_id, cx) {
6685 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6686 let mut new_selection = s.newest_anchor().clone();
6687 new_selection.collapse_to(jump_to, SelectionGoal::None);
6688 s.select_anchors(vec![new_selection.clone()]);
6689 });
6690 }
6691 return;
6692 }
6693 }
6694
6695 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6696 active_diagnostics
6697 .primary_range
6698 .to_offset(&buffer)
6699 .to_inclusive()
6700 });
6701 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6702 if active_primary_range.contains(&selection.head()) {
6703 *active_primary_range.end()
6704 } else {
6705 selection.head()
6706 }
6707 } else {
6708 selection.head()
6709 };
6710
6711 loop {
6712 let mut diagnostics = if direction == Direction::Prev {
6713 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6714 } else {
6715 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6716 };
6717 let group = diagnostics.find_map(|entry| {
6718 if entry.diagnostic.is_primary
6719 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6720 && !entry.range.is_empty()
6721 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6722 {
6723 Some((entry.range, entry.diagnostic.group_id))
6724 } else {
6725 None
6726 }
6727 });
6728
6729 if let Some((primary_range, group_id)) = group {
6730 if self.activate_diagnostics(group_id, cx) {
6731 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6732 s.select(vec![Selection {
6733 id: selection.id,
6734 start: primary_range.start,
6735 end: primary_range.start,
6736 reversed: false,
6737 goal: SelectionGoal::None,
6738 }]);
6739 });
6740 }
6741 break;
6742 } else {
6743 // Cycle around to the start of the buffer, potentially moving back to the start of
6744 // the currently active diagnostic.
6745 active_primary_range.take();
6746 if direction == Direction::Prev {
6747 if search_start == buffer.len() {
6748 break;
6749 } else {
6750 search_start = buffer.len();
6751 }
6752 } else if search_start == 0 {
6753 break;
6754 } else {
6755 search_start = 0;
6756 }
6757 }
6758 }
6759 }
6760
6761 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6762 let snapshot = self
6763 .display_map
6764 .update(cx, |display_map, cx| display_map.snapshot(cx));
6765 let selection = self.selections.newest::<Point>(cx);
6766
6767 if !self.seek_in_direction(
6768 &snapshot,
6769 selection.head(),
6770 false,
6771 snapshot
6772 .buffer_snapshot
6773 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6774 cx,
6775 ) {
6776 let wrapped_point = Point::zero();
6777 self.seek_in_direction(
6778 &snapshot,
6779 wrapped_point,
6780 true,
6781 snapshot
6782 .buffer_snapshot
6783 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6784 cx,
6785 );
6786 }
6787 }
6788
6789 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6790 let snapshot = self
6791 .display_map
6792 .update(cx, |display_map, cx| display_map.snapshot(cx));
6793 let selection = self.selections.newest::<Point>(cx);
6794
6795 if !self.seek_in_direction(
6796 &snapshot,
6797 selection.head(),
6798 false,
6799 snapshot
6800 .buffer_snapshot
6801 .git_diff_hunks_in_range_rev(0..selection.head().row),
6802 cx,
6803 ) {
6804 let wrapped_point = snapshot.buffer_snapshot.max_point();
6805 self.seek_in_direction(
6806 &snapshot,
6807 wrapped_point,
6808 true,
6809 snapshot
6810 .buffer_snapshot
6811 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6812 cx,
6813 );
6814 }
6815 }
6816
6817 fn seek_in_direction(
6818 &mut self,
6819 snapshot: &DisplaySnapshot,
6820 initial_point: Point,
6821 is_wrapped: bool,
6822 hunks: impl Iterator<Item = DiffHunk<u32>>,
6823 cx: &mut ViewContext<Editor>,
6824 ) -> bool {
6825 let display_point = initial_point.to_display_point(snapshot);
6826 let mut hunks = hunks
6827 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6828 .skip_while(|hunk| {
6829 if is_wrapped {
6830 false
6831 } else {
6832 hunk.contains_display_row(display_point.row())
6833 }
6834 })
6835 .dedup();
6836
6837 if let Some(hunk) = hunks.next() {
6838 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6839 let row = hunk.start_display_row();
6840 let point = DisplayPoint::new(row, 0);
6841 s.select_display_ranges([point..point]);
6842 });
6843
6844 true
6845 } else {
6846 false
6847 }
6848 }
6849
6850 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6851 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6852 }
6853
6854 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6855 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6856 }
6857
6858 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6859 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6860 }
6861
6862 pub fn go_to_type_definition_split(
6863 &mut self,
6864 _: &GoToTypeDefinitionSplit,
6865 cx: &mut ViewContext<Self>,
6866 ) {
6867 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6868 }
6869
6870 fn go_to_definition_of_kind(
6871 &mut self,
6872 kind: GotoDefinitionKind,
6873 split: bool,
6874 cx: &mut ViewContext<Self>,
6875 ) {
6876 let Some(workspace) = self.workspace(cx) else {
6877 return;
6878 };
6879 let buffer = self.buffer.read(cx);
6880 let head = self.selections.newest::<usize>(cx).head();
6881 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6882 text_anchor
6883 } else {
6884 return;
6885 };
6886
6887 let project = workspace.read(cx).project().clone();
6888 let definitions = project.update(cx, |project, cx| match kind {
6889 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6890 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6891 });
6892
6893 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6894 let definitions = definitions.await?;
6895 editor.update(&mut cx, |editor, cx| {
6896 editor.navigate_to_definitions(
6897 definitions
6898 .into_iter()
6899 .map(GoToDefinitionLink::Text)
6900 .collect(),
6901 split,
6902 cx,
6903 );
6904 })?;
6905 Ok::<(), anyhow::Error>(())
6906 })
6907 .detach_and_log_err(cx);
6908 }
6909
6910 pub fn navigate_to_definitions(
6911 &mut self,
6912 mut definitions: Vec<GoToDefinitionLink>,
6913 split: bool,
6914 cx: &mut ViewContext<Editor>,
6915 ) {
6916 let Some(workspace) = self.workspace(cx) else {
6917 return;
6918 };
6919 let pane = workspace.read(cx).active_pane().clone();
6920 // If there is one definition, just open it directly
6921 if definitions.len() == 1 {
6922 let definition = definitions.pop().unwrap();
6923 let target_task = match definition {
6924 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
6925 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6926 self.compute_target_location(lsp_location, server_id, cx)
6927 }
6928 };
6929 cx.spawn(|editor, mut cx| async move {
6930 let target = target_task.await.context("target resolution task")?;
6931 if let Some(target) = target {
6932 editor.update(&mut cx, |editor, cx| {
6933 let range = target.range.to_offset(target.buffer.read(cx));
6934 let range = editor.range_for_match(&range);
6935 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
6936 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6937 s.select_ranges([range]);
6938 });
6939 } else {
6940 cx.window_context().defer(move |cx| {
6941 let target_editor: ViewHandle<Self> =
6942 workspace.update(cx, |workspace, cx| {
6943 if split {
6944 workspace.split_project_item(target.buffer.clone(), cx)
6945 } else {
6946 workspace.open_project_item(target.buffer.clone(), cx)
6947 }
6948 });
6949 target_editor.update(cx, |target_editor, cx| {
6950 // When selecting a definition in a different buffer, disable the nav history
6951 // to avoid creating a history entry at the previous cursor location.
6952 pane.update(cx, |pane, _| pane.disable_history());
6953 target_editor.change_selections(
6954 Some(Autoscroll::fit()),
6955 cx,
6956 |s| {
6957 s.select_ranges([range]);
6958 },
6959 );
6960 pane.update(cx, |pane, _| pane.enable_history());
6961 });
6962 });
6963 }
6964 })
6965 } else {
6966 Ok(())
6967 }
6968 })
6969 .detach_and_log_err(cx);
6970 } else if !definitions.is_empty() {
6971 let replica_id = self.replica_id(cx);
6972 cx.spawn(|editor, mut cx| async move {
6973 let (title, location_tasks) = editor
6974 .update(&mut cx, |editor, cx| {
6975 let title = definitions
6976 .iter()
6977 .find_map(|definition| match definition {
6978 GoToDefinitionLink::Text(link) => {
6979 link.origin.as_ref().map(|origin| {
6980 let buffer = origin.buffer.read(cx);
6981 format!(
6982 "Definitions for {}",
6983 buffer
6984 .text_for_range(origin.range.clone())
6985 .collect::<String>()
6986 )
6987 })
6988 }
6989 GoToDefinitionLink::InlayHint(_, _) => None,
6990 })
6991 .unwrap_or("Definitions".to_string());
6992 let location_tasks = definitions
6993 .into_iter()
6994 .map(|definition| match definition {
6995 GoToDefinitionLink::Text(link) => {
6996 Task::Ready(Some(Ok(Some(link.target))))
6997 }
6998 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6999 editor.compute_target_location(lsp_location, server_id, cx)
7000 }
7001 })
7002 .collect::<Vec<_>>();
7003 (title, location_tasks)
7004 })
7005 .context("location tasks preparation")?;
7006
7007 let locations = futures::future::join_all(location_tasks)
7008 .await
7009 .into_iter()
7010 .filter_map(|location| location.transpose())
7011 .collect::<Result<_>>()
7012 .context("location tasks")?;
7013 workspace.update(&mut cx, |workspace, cx| {
7014 Self::open_locations_in_multibuffer(
7015 workspace, locations, replica_id, title, split, cx,
7016 )
7017 });
7018
7019 anyhow::Ok(())
7020 })
7021 .detach_and_log_err(cx);
7022 }
7023 }
7024
7025 fn compute_target_location(
7026 &self,
7027 lsp_location: lsp::Location,
7028 server_id: LanguageServerId,
7029 cx: &mut ViewContext<Editor>,
7030 ) -> Task<anyhow::Result<Option<Location>>> {
7031 let Some(project) = self.project.clone() else {
7032 return Task::Ready(Some(Ok(None)));
7033 };
7034
7035 cx.spawn(move |editor, mut cx| async move {
7036 let location_task = editor.update(&mut cx, |editor, cx| {
7037 project.update(cx, |project, cx| {
7038 let language_server_name =
7039 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7040 project
7041 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7042 .map(|(_, lsp_adapter)| {
7043 LanguageServerName(Arc::from(lsp_adapter.name()))
7044 })
7045 });
7046 language_server_name.map(|language_server_name| {
7047 project.open_local_buffer_via_lsp(
7048 lsp_location.uri.clone(),
7049 server_id,
7050 language_server_name,
7051 cx,
7052 )
7053 })
7054 })
7055 })?;
7056 let location = match location_task {
7057 Some(task) => Some({
7058 let target_buffer_handle = task.await.context("open local buffer")?;
7059 let range = {
7060 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7061 let target_start = target_buffer.clip_point_utf16(
7062 point_from_lsp(lsp_location.range.start),
7063 Bias::Left,
7064 );
7065 let target_end = target_buffer.clip_point_utf16(
7066 point_from_lsp(lsp_location.range.end),
7067 Bias::Left,
7068 );
7069 target_buffer.anchor_after(target_start)
7070 ..target_buffer.anchor_before(target_end)
7071 })
7072 };
7073 Location {
7074 buffer: target_buffer_handle,
7075 range,
7076 }
7077 }),
7078 None => None,
7079 };
7080 Ok(location)
7081 })
7082 }
7083
7084 pub fn find_all_references(
7085 workspace: &mut Workspace,
7086 _: &FindAllReferences,
7087 cx: &mut ViewContext<Workspace>,
7088 ) -> Option<Task<Result<()>>> {
7089 let active_item = workspace.active_item(cx)?;
7090 let editor_handle = active_item.act_as::<Self>(cx)?;
7091
7092 let editor = editor_handle.read(cx);
7093 let buffer = editor.buffer.read(cx);
7094 let head = editor.selections.newest::<usize>(cx).head();
7095 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7096 let replica_id = editor.replica_id(cx);
7097
7098 let project = workspace.project().clone();
7099 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7100 Some(cx.spawn_labeled(
7101 "Finding All References...",
7102 |workspace, mut cx| async move {
7103 let locations = references.await?;
7104 if locations.is_empty() {
7105 return Ok(());
7106 }
7107
7108 workspace.update(&mut cx, |workspace, cx| {
7109 let title = locations
7110 .first()
7111 .as_ref()
7112 .map(|location| {
7113 let buffer = location.buffer.read(cx);
7114 format!(
7115 "References to `{}`",
7116 buffer
7117 .text_for_range(location.range.clone())
7118 .collect::<String>()
7119 )
7120 })
7121 .unwrap();
7122 Self::open_locations_in_multibuffer(
7123 workspace, locations, replica_id, title, false, cx,
7124 );
7125 })?;
7126
7127 Ok(())
7128 },
7129 ))
7130 }
7131
7132 /// Opens a multibuffer with the given project locations in it
7133 pub fn open_locations_in_multibuffer(
7134 workspace: &mut Workspace,
7135 mut locations: Vec<Location>,
7136 replica_id: ReplicaId,
7137 title: String,
7138 split: bool,
7139 cx: &mut ViewContext<Workspace>,
7140 ) {
7141 // If there are multiple definitions, open them in a multibuffer
7142 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7143 let mut locations = locations.into_iter().peekable();
7144 let mut ranges_to_highlight = Vec::new();
7145
7146 let excerpt_buffer = cx.add_model(|cx| {
7147 let mut multibuffer = MultiBuffer::new(replica_id);
7148 while let Some(location) = locations.next() {
7149 let buffer = location.buffer.read(cx);
7150 let mut ranges_for_buffer = Vec::new();
7151 let range = location.range.to_offset(buffer);
7152 ranges_for_buffer.push(range.clone());
7153
7154 while let Some(next_location) = locations.peek() {
7155 if next_location.buffer == location.buffer {
7156 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7157 locations.next();
7158 } else {
7159 break;
7160 }
7161 }
7162
7163 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7164 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7165 location.buffer.clone(),
7166 ranges_for_buffer,
7167 1,
7168 cx,
7169 ))
7170 }
7171
7172 multibuffer.with_title(title)
7173 });
7174
7175 let editor = cx.add_view(|cx| {
7176 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7177 });
7178 editor.update(cx, |editor, cx| {
7179 editor.highlight_background::<Self>(
7180 ranges_to_highlight,
7181 |theme| theme.editor.highlighted_line_background,
7182 cx,
7183 );
7184 });
7185 if split {
7186 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7187 } else {
7188 workspace.add_item(Box::new(editor), cx);
7189 }
7190 }
7191
7192 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7193 use language::ToOffset as _;
7194
7195 let project = self.project.clone()?;
7196 let selection = self.selections.newest_anchor().clone();
7197 let (cursor_buffer, cursor_buffer_position) = self
7198 .buffer
7199 .read(cx)
7200 .text_anchor_for_position(selection.head(), cx)?;
7201 let (tail_buffer, _) = self
7202 .buffer
7203 .read(cx)
7204 .text_anchor_for_position(selection.tail(), cx)?;
7205 if tail_buffer != cursor_buffer {
7206 return None;
7207 }
7208
7209 let snapshot = cursor_buffer.read(cx).snapshot();
7210 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7211 let prepare_rename = project.update(cx, |project, cx| {
7212 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7213 });
7214
7215 Some(cx.spawn(|this, mut cx| async move {
7216 let rename_range = if let Some(range) = prepare_rename.await? {
7217 Some(range)
7218 } else {
7219 this.update(&mut cx, |this, cx| {
7220 let buffer = this.buffer.read(cx).snapshot(cx);
7221 let mut buffer_highlights = this
7222 .document_highlights_for_position(selection.head(), &buffer)
7223 .filter(|highlight| {
7224 highlight.start.excerpt_id() == selection.head().excerpt_id()
7225 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7226 });
7227 buffer_highlights
7228 .next()
7229 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7230 })?
7231 };
7232 if let Some(rename_range) = rename_range {
7233 let rename_buffer_range = rename_range.to_offset(&snapshot);
7234 let cursor_offset_in_rename_range =
7235 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7236
7237 this.update(&mut cx, |this, cx| {
7238 this.take_rename(false, cx);
7239 let style = this.style(cx);
7240 let buffer = this.buffer.read(cx).read(cx);
7241 let cursor_offset = selection.head().to_offset(&buffer);
7242 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7243 let rename_end = rename_start + rename_buffer_range.len();
7244 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7245 let mut old_highlight_id = None;
7246 let old_name: Arc<str> = buffer
7247 .chunks(rename_start..rename_end, true)
7248 .map(|chunk| {
7249 if old_highlight_id.is_none() {
7250 old_highlight_id = chunk.syntax_highlight_id;
7251 }
7252 chunk.text
7253 })
7254 .collect::<String>()
7255 .into();
7256
7257 drop(buffer);
7258
7259 // Position the selection in the rename editor so that it matches the current selection.
7260 this.show_local_selections = false;
7261 let rename_editor = cx.add_view(|cx| {
7262 let mut editor = Editor::single_line(None, cx);
7263 if let Some(old_highlight_id) = old_highlight_id {
7264 editor.override_text_style =
7265 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7266 }
7267 editor.buffer.update(cx, |buffer, cx| {
7268 buffer.edit([(0..0, old_name.clone())], None, cx)
7269 });
7270 editor.select_all(&SelectAll, cx);
7271 editor
7272 });
7273
7274 let ranges = this
7275 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7276 .into_iter()
7277 .flat_map(|(_, ranges)| ranges.into_iter())
7278 .chain(
7279 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7280 .into_iter()
7281 .flat_map(|(_, ranges)| ranges.into_iter()),
7282 )
7283 .collect();
7284
7285 this.highlight_text::<Rename>(
7286 ranges,
7287 HighlightStyle {
7288 fade_out: Some(style.rename_fade),
7289 ..Default::default()
7290 },
7291 cx,
7292 );
7293 cx.focus(&rename_editor);
7294 let block_id = this.insert_blocks(
7295 [BlockProperties {
7296 style: BlockStyle::Flex,
7297 position: range.start.clone(),
7298 height: 1,
7299 render: Arc::new({
7300 let editor = rename_editor.clone();
7301 move |cx: &mut BlockContext| {
7302 ChildView::new(&editor, cx)
7303 .contained()
7304 .with_padding_left(cx.anchor_x)
7305 .into_any()
7306 }
7307 }),
7308 disposition: BlockDisposition::Below,
7309 }],
7310 Some(Autoscroll::fit()),
7311 cx,
7312 )[0];
7313 this.pending_rename = Some(RenameState {
7314 range,
7315 old_name,
7316 editor: rename_editor,
7317 block_id,
7318 });
7319 })?;
7320 }
7321
7322 Ok(())
7323 }))
7324 }
7325
7326 pub fn confirm_rename(
7327 workspace: &mut Workspace,
7328 _: &ConfirmRename,
7329 cx: &mut ViewContext<Workspace>,
7330 ) -> Option<Task<Result<()>>> {
7331 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7332
7333 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7334 let rename = editor.take_rename(false, cx)?;
7335 let buffer = editor.buffer.read(cx);
7336 let (start_buffer, start) =
7337 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7338 let (end_buffer, end) =
7339 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7340 if start_buffer == end_buffer {
7341 let new_name = rename.editor.read(cx).text(cx);
7342 Some((start_buffer, start..end, rename.old_name, new_name))
7343 } else {
7344 None
7345 }
7346 })?;
7347
7348 let rename = workspace.project().clone().update(cx, |project, cx| {
7349 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7350 });
7351
7352 let editor = editor.downgrade();
7353 Some(cx.spawn(|workspace, mut cx| async move {
7354 let project_transaction = rename.await?;
7355 Self::open_project_transaction(
7356 &editor,
7357 workspace,
7358 project_transaction,
7359 format!("Rename: {} → {}", old_name, new_name),
7360 cx.clone(),
7361 )
7362 .await?;
7363
7364 editor.update(&mut cx, |editor, cx| {
7365 editor.refresh_document_highlights(cx);
7366 })?;
7367 Ok(())
7368 }))
7369 }
7370
7371 fn take_rename(
7372 &mut self,
7373 moving_cursor: bool,
7374 cx: &mut ViewContext<Self>,
7375 ) -> Option<RenameState> {
7376 let rename = self.pending_rename.take()?;
7377 self.remove_blocks(
7378 [rename.block_id].into_iter().collect(),
7379 Some(Autoscroll::fit()),
7380 cx,
7381 );
7382 self.clear_highlights::<Rename>(cx);
7383 self.show_local_selections = true;
7384
7385 if moving_cursor {
7386 let rename_editor = rename.editor.read(cx);
7387 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7388
7389 // Update the selection to match the position of the selection inside
7390 // the rename editor.
7391 let snapshot = self.buffer.read(cx).read(cx);
7392 let rename_range = rename.range.to_offset(&snapshot);
7393 let cursor_in_editor = snapshot
7394 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7395 .min(rename_range.end);
7396 drop(snapshot);
7397
7398 self.change_selections(None, cx, |s| {
7399 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7400 });
7401 } else {
7402 self.refresh_document_highlights(cx);
7403 }
7404
7405 Some(rename)
7406 }
7407
7408 #[cfg(any(test, feature = "test-support"))]
7409 pub fn pending_rename(&self) -> Option<&RenameState> {
7410 self.pending_rename.as_ref()
7411 }
7412
7413 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7414 let project = match &self.project {
7415 Some(project) => project.clone(),
7416 None => return None,
7417 };
7418
7419 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7420 }
7421
7422 fn perform_format(
7423 &mut self,
7424 project: ModelHandle<Project>,
7425 trigger: FormatTrigger,
7426 cx: &mut ViewContext<Self>,
7427 ) -> Task<Result<()>> {
7428 let buffer = self.buffer().clone();
7429 let buffers = buffer.read(cx).all_buffers();
7430
7431 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7432 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7433
7434 cx.spawn(|_, mut cx| async move {
7435 let transaction = futures::select_biased! {
7436 _ = timeout => {
7437 log::warn!("timed out waiting for formatting");
7438 None
7439 }
7440 transaction = format.log_err().fuse() => transaction,
7441 };
7442
7443 buffer.update(&mut cx, |buffer, cx| {
7444 if let Some(transaction) = transaction {
7445 if !buffer.is_singleton() {
7446 buffer.push_transaction(&transaction.0, cx);
7447 }
7448 }
7449
7450 cx.notify();
7451 });
7452
7453 Ok(())
7454 })
7455 }
7456
7457 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7458 if let Some(project) = self.project.clone() {
7459 self.buffer.update(cx, |multi_buffer, cx| {
7460 project.update(cx, |project, cx| {
7461 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7462 });
7463 })
7464 }
7465 }
7466
7467 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7468 cx.show_character_palette();
7469 }
7470
7471 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7472 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7473 let buffer = self.buffer.read(cx).snapshot(cx);
7474 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7475 let is_valid = buffer
7476 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7477 .any(|entry| {
7478 entry.diagnostic.is_primary
7479 && !entry.range.is_empty()
7480 && entry.range.start == primary_range_start
7481 && entry.diagnostic.message == active_diagnostics.primary_message
7482 });
7483
7484 if is_valid != active_diagnostics.is_valid {
7485 active_diagnostics.is_valid = is_valid;
7486 let mut new_styles = HashMap::default();
7487 for (block_id, diagnostic) in &active_diagnostics.blocks {
7488 new_styles.insert(
7489 *block_id,
7490 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7491 );
7492 }
7493 self.display_map
7494 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7495 }
7496 }
7497 }
7498
7499 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7500 self.dismiss_diagnostics(cx);
7501 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7502 let buffer = self.buffer.read(cx).snapshot(cx);
7503
7504 let mut primary_range = None;
7505 let mut primary_message = None;
7506 let mut group_end = Point::zero();
7507 let diagnostic_group = buffer
7508 .diagnostic_group::<Point>(group_id)
7509 .map(|entry| {
7510 if entry.range.end > group_end {
7511 group_end = entry.range.end;
7512 }
7513 if entry.diagnostic.is_primary {
7514 primary_range = Some(entry.range.clone());
7515 primary_message = Some(entry.diagnostic.message.clone());
7516 }
7517 entry
7518 })
7519 .collect::<Vec<_>>();
7520 let primary_range = primary_range?;
7521 let primary_message = primary_message?;
7522 let primary_range =
7523 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7524
7525 let blocks = display_map
7526 .insert_blocks(
7527 diagnostic_group.iter().map(|entry| {
7528 let diagnostic = entry.diagnostic.clone();
7529 let message_height = diagnostic.message.lines().count() as u8;
7530 BlockProperties {
7531 style: BlockStyle::Fixed,
7532 position: buffer.anchor_after(entry.range.start),
7533 height: message_height,
7534 render: diagnostic_block_renderer(diagnostic, true),
7535 disposition: BlockDisposition::Below,
7536 }
7537 }),
7538 cx,
7539 )
7540 .into_iter()
7541 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7542 .collect();
7543
7544 Some(ActiveDiagnosticGroup {
7545 primary_range,
7546 primary_message,
7547 blocks,
7548 is_valid: true,
7549 })
7550 });
7551 self.active_diagnostics.is_some()
7552 }
7553
7554 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7555 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7556 self.display_map.update(cx, |display_map, cx| {
7557 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7558 });
7559 cx.notify();
7560 }
7561 }
7562
7563 pub fn set_selections_from_remote(
7564 &mut self,
7565 selections: Vec<Selection<Anchor>>,
7566 pending_selection: Option<Selection<Anchor>>,
7567 cx: &mut ViewContext<Self>,
7568 ) {
7569 let old_cursor_position = self.selections.newest_anchor().head();
7570 self.selections.change_with(cx, |s| {
7571 s.select_anchors(selections);
7572 if let Some(pending_selection) = pending_selection {
7573 s.set_pending(pending_selection, SelectMode::Character);
7574 } else {
7575 s.clear_pending();
7576 }
7577 });
7578 self.selections_did_change(false, &old_cursor_position, cx);
7579 }
7580
7581 fn push_to_selection_history(&mut self) {
7582 self.selection_history.push(SelectionHistoryEntry {
7583 selections: self.selections.disjoint_anchors(),
7584 select_next_state: self.select_next_state.clone(),
7585 select_prev_state: self.select_prev_state.clone(),
7586 add_selections_state: self.add_selections_state.clone(),
7587 });
7588 }
7589
7590 pub fn transact(
7591 &mut self,
7592 cx: &mut ViewContext<Self>,
7593 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7594 ) -> Option<TransactionId> {
7595 self.start_transaction_at(Instant::now(), cx);
7596 update(self, cx);
7597 self.end_transaction_at(Instant::now(), cx)
7598 }
7599
7600 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7601 self.end_selection(cx);
7602 if let Some(tx_id) = self
7603 .buffer
7604 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7605 {
7606 self.selection_history
7607 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7608 }
7609 }
7610
7611 fn end_transaction_at(
7612 &mut self,
7613 now: Instant,
7614 cx: &mut ViewContext<Self>,
7615 ) -> Option<TransactionId> {
7616 if let Some(tx_id) = self
7617 .buffer
7618 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7619 {
7620 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7621 *end_selections = Some(self.selections.disjoint_anchors());
7622 } else {
7623 error!("unexpectedly ended a transaction that wasn't started by this editor");
7624 }
7625
7626 cx.emit(Event::Edited);
7627 Some(tx_id)
7628 } else {
7629 None
7630 }
7631 }
7632
7633 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7634 let mut fold_ranges = Vec::new();
7635
7636 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7637
7638 let selections = self.selections.all_adjusted(cx);
7639 for selection in selections {
7640 let range = selection.range().sorted();
7641 let buffer_start_row = range.start.row;
7642
7643 for row in (0..=range.end.row).rev() {
7644 let fold_range = display_map.foldable_range(row);
7645
7646 if let Some(fold_range) = fold_range {
7647 if fold_range.end.row >= buffer_start_row {
7648 fold_ranges.push(fold_range);
7649 if row <= range.start.row {
7650 break;
7651 }
7652 }
7653 }
7654 }
7655 }
7656
7657 self.fold_ranges(fold_ranges, true, cx);
7658 }
7659
7660 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7661 let buffer_row = fold_at.buffer_row;
7662 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7663
7664 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7665 let autoscroll = self
7666 .selections
7667 .all::<Point>(cx)
7668 .iter()
7669 .any(|selection| fold_range.overlaps(&selection.range()));
7670
7671 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7672 }
7673 }
7674
7675 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7676 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7677 let buffer = &display_map.buffer_snapshot;
7678 let selections = self.selections.all::<Point>(cx);
7679 let ranges = selections
7680 .iter()
7681 .map(|s| {
7682 let range = s.display_range(&display_map).sorted();
7683 let mut start = range.start.to_point(&display_map);
7684 let mut end = range.end.to_point(&display_map);
7685 start.column = 0;
7686 end.column = buffer.line_len(end.row);
7687 start..end
7688 })
7689 .collect::<Vec<_>>();
7690
7691 self.unfold_ranges(ranges, true, true, cx);
7692 }
7693
7694 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7695 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7696
7697 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7698 ..Point::new(
7699 unfold_at.buffer_row,
7700 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7701 );
7702
7703 let autoscroll = self
7704 .selections
7705 .all::<Point>(cx)
7706 .iter()
7707 .any(|selection| selection.range().overlaps(&intersection_range));
7708
7709 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7710 }
7711
7712 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7713 let selections = self.selections.all::<Point>(cx);
7714 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7715 let line_mode = self.selections.line_mode;
7716 let ranges = selections.into_iter().map(|s| {
7717 if line_mode {
7718 let start = Point::new(s.start.row, 0);
7719 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
7720 start..end
7721 } else {
7722 s.start..s.end
7723 }
7724 });
7725 self.fold_ranges(ranges, true, cx);
7726 }
7727
7728 pub fn fold_ranges<T: ToOffset + Clone>(
7729 &mut self,
7730 ranges: impl IntoIterator<Item = Range<T>>,
7731 auto_scroll: bool,
7732 cx: &mut ViewContext<Self>,
7733 ) {
7734 let mut ranges = ranges.into_iter().peekable();
7735 if ranges.peek().is_some() {
7736 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7737
7738 if auto_scroll {
7739 self.request_autoscroll(Autoscroll::fit(), cx);
7740 }
7741
7742 cx.notify();
7743 }
7744 }
7745
7746 pub fn unfold_ranges<T: ToOffset + Clone>(
7747 &mut self,
7748 ranges: impl IntoIterator<Item = Range<T>>,
7749 inclusive: bool,
7750 auto_scroll: bool,
7751 cx: &mut ViewContext<Self>,
7752 ) {
7753 let mut ranges = ranges.into_iter().peekable();
7754 if ranges.peek().is_some() {
7755 self.display_map
7756 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7757 if auto_scroll {
7758 self.request_autoscroll(Autoscroll::fit(), cx);
7759 }
7760
7761 cx.notify();
7762 }
7763 }
7764
7765 pub fn gutter_hover(
7766 &mut self,
7767 GutterHover { hovered }: &GutterHover,
7768 cx: &mut ViewContext<Self>,
7769 ) {
7770 self.gutter_hovered = *hovered;
7771 cx.notify();
7772 }
7773
7774 pub fn insert_blocks(
7775 &mut self,
7776 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7777 autoscroll: Option<Autoscroll>,
7778 cx: &mut ViewContext<Self>,
7779 ) -> Vec<BlockId> {
7780 let blocks = self
7781 .display_map
7782 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7783 if let Some(autoscroll) = autoscroll {
7784 self.request_autoscroll(autoscroll, cx);
7785 }
7786 blocks
7787 }
7788
7789 pub fn replace_blocks(
7790 &mut self,
7791 blocks: HashMap<BlockId, RenderBlock>,
7792 autoscroll: Option<Autoscroll>,
7793 cx: &mut ViewContext<Self>,
7794 ) {
7795 self.display_map
7796 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7797 if let Some(autoscroll) = autoscroll {
7798 self.request_autoscroll(autoscroll, cx);
7799 }
7800 }
7801
7802 pub fn remove_blocks(
7803 &mut self,
7804 block_ids: HashSet<BlockId>,
7805 autoscroll: Option<Autoscroll>,
7806 cx: &mut ViewContext<Self>,
7807 ) {
7808 self.display_map.update(cx, |display_map, cx| {
7809 display_map.remove_blocks(block_ids, cx)
7810 });
7811 if let Some(autoscroll) = autoscroll {
7812 self.request_autoscroll(autoscroll, cx);
7813 }
7814 }
7815
7816 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7817 self.display_map
7818 .update(cx, |map, cx| map.snapshot(cx))
7819 .longest_row()
7820 }
7821
7822 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7823 self.display_map
7824 .update(cx, |map, cx| map.snapshot(cx))
7825 .max_point()
7826 }
7827
7828 pub fn text(&self, cx: &AppContext) -> String {
7829 self.buffer.read(cx).read(cx).text()
7830 }
7831
7832 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7833 self.transact(cx, |this, cx| {
7834 this.buffer
7835 .read(cx)
7836 .as_singleton()
7837 .expect("you can only call set_text on editors for singleton buffers")
7838 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7839 });
7840 }
7841
7842 pub fn display_text(&self, cx: &mut AppContext) -> String {
7843 self.display_map
7844 .update(cx, |map, cx| map.snapshot(cx))
7845 .text()
7846 }
7847
7848 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7849 let mut wrap_guides = smallvec::smallvec![];
7850
7851 if self.show_wrap_guides == Some(false) {
7852 return wrap_guides;
7853 }
7854
7855 let settings = self.buffer.read(cx).settings_at(0, cx);
7856 if settings.show_wrap_guides {
7857 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7858 wrap_guides.push((soft_wrap as usize, true));
7859 }
7860 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7861 }
7862
7863 wrap_guides
7864 }
7865
7866 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7867 let settings = self.buffer.read(cx).settings_at(0, cx);
7868 let mode = self
7869 .soft_wrap_mode_override
7870 .unwrap_or_else(|| settings.soft_wrap);
7871 match mode {
7872 language_settings::SoftWrap::None => SoftWrap::None,
7873 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7874 language_settings::SoftWrap::PreferredLineLength => {
7875 SoftWrap::Column(settings.preferred_line_length)
7876 }
7877 }
7878 }
7879
7880 pub fn set_soft_wrap_mode(
7881 &mut self,
7882 mode: language_settings::SoftWrap,
7883 cx: &mut ViewContext<Self>,
7884 ) {
7885 self.soft_wrap_mode_override = Some(mode);
7886 cx.notify();
7887 }
7888
7889 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7890 self.display_map
7891 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7892 }
7893
7894 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7895 if self.soft_wrap_mode_override.is_some() {
7896 self.soft_wrap_mode_override.take();
7897 } else {
7898 let soft_wrap = match self.soft_wrap_mode(cx) {
7899 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7900 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7901 };
7902 self.soft_wrap_mode_override = Some(soft_wrap);
7903 }
7904 cx.notify();
7905 }
7906
7907 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7908 self.show_gutter = show_gutter;
7909 cx.notify();
7910 }
7911
7912 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7913 self.show_wrap_guides = Some(show_gutter);
7914 cx.notify();
7915 }
7916
7917 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7918 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7919 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7920 cx.reveal_path(&file.abs_path(cx));
7921 }
7922 }
7923 }
7924
7925 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7926 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7927 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7928 if let Some(path) = file.abs_path(cx).to_str() {
7929 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7930 }
7931 }
7932 }
7933 }
7934
7935 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7936 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7937 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7938 if let Some(path) = file.path().to_str() {
7939 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7940 }
7941 }
7942 }
7943 }
7944
7945 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7946 self.highlighted_rows = rows;
7947 }
7948
7949 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7950 self.highlighted_rows.clone()
7951 }
7952
7953 pub fn highlight_background<T: 'static>(
7954 &mut self,
7955 ranges: Vec<Range<Anchor>>,
7956 color_fetcher: fn(&Theme) -> Color,
7957 cx: &mut ViewContext<Self>,
7958 ) {
7959 self.background_highlights
7960 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7961 cx.notify();
7962 }
7963
7964 pub fn highlight_inlay_background<T: 'static>(
7965 &mut self,
7966 ranges: Vec<InlayHighlight>,
7967 color_fetcher: fn(&Theme) -> Color,
7968 cx: &mut ViewContext<Self>,
7969 ) {
7970 // TODO: no actual highlights happen for inlays currently, find a way to do that
7971 self.inlay_background_highlights
7972 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
7973 cx.notify();
7974 }
7975
7976 pub fn clear_background_highlights<T: 'static>(
7977 &mut self,
7978 cx: &mut ViewContext<Self>,
7979 ) -> Option<BackgroundHighlight> {
7980 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
7981 let inlay_highlights = self
7982 .inlay_background_highlights
7983 .remove(&Some(TypeId::of::<T>()));
7984 if text_highlights.is_some() || inlay_highlights.is_some() {
7985 cx.notify();
7986 }
7987 text_highlights
7988 }
7989
7990 #[cfg(feature = "test-support")]
7991 pub fn all_text_background_highlights(
7992 &mut self,
7993 cx: &mut ViewContext<Self>,
7994 ) -> Vec<(Range<DisplayPoint>, Color)> {
7995 let snapshot = self.snapshot(cx);
7996 let buffer = &snapshot.buffer_snapshot;
7997 let start = buffer.anchor_before(0);
7998 let end = buffer.anchor_after(buffer.len());
7999 let theme = theme::current(cx);
8000 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
8001 }
8002
8003 fn document_highlights_for_position<'a>(
8004 &'a self,
8005 position: Anchor,
8006 buffer: &'a MultiBufferSnapshot,
8007 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8008 let read_highlights = self
8009 .background_highlights
8010 .get(&TypeId::of::<DocumentHighlightRead>())
8011 .map(|h| &h.1);
8012 let write_highlights = self
8013 .background_highlights
8014 .get(&TypeId::of::<DocumentHighlightWrite>())
8015 .map(|h| &h.1);
8016 let left_position = position.bias_left(buffer);
8017 let right_position = position.bias_right(buffer);
8018 read_highlights
8019 .into_iter()
8020 .chain(write_highlights)
8021 .flat_map(move |ranges| {
8022 let start_ix = match ranges.binary_search_by(|probe| {
8023 let cmp = probe.end.cmp(&left_position, buffer);
8024 if cmp.is_ge() {
8025 Ordering::Greater
8026 } else {
8027 Ordering::Less
8028 }
8029 }) {
8030 Ok(i) | Err(i) => i,
8031 };
8032
8033 let right_position = right_position.clone();
8034 ranges[start_ix..]
8035 .iter()
8036 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8037 })
8038 }
8039
8040 pub fn background_highlights_in_range(
8041 &self,
8042 search_range: Range<Anchor>,
8043 display_snapshot: &DisplaySnapshot,
8044 theme: &Theme,
8045 ) -> Vec<(Range<DisplayPoint>, Color)> {
8046 let mut results = Vec::new();
8047 for (color_fetcher, ranges) in self.background_highlights.values() {
8048 let color = color_fetcher(theme);
8049 let start_ix = match ranges.binary_search_by(|probe| {
8050 let cmp = probe
8051 .end
8052 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8053 if cmp.is_gt() {
8054 Ordering::Greater
8055 } else {
8056 Ordering::Less
8057 }
8058 }) {
8059 Ok(i) | Err(i) => i,
8060 };
8061 for range in &ranges[start_ix..] {
8062 if range
8063 .start
8064 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8065 .is_ge()
8066 {
8067 break;
8068 }
8069
8070 let start = range.start.to_display_point(&display_snapshot);
8071 let end = range.end.to_display_point(&display_snapshot);
8072 results.push((start..end, color))
8073 }
8074 }
8075 results
8076 }
8077
8078 pub fn background_highlight_row_ranges<T: 'static>(
8079 &self,
8080 search_range: Range<Anchor>,
8081 display_snapshot: &DisplaySnapshot,
8082 count: usize,
8083 ) -> Vec<RangeInclusive<DisplayPoint>> {
8084 let mut results = Vec::new();
8085 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8086 return vec![];
8087 };
8088
8089 let start_ix = match ranges.binary_search_by(|probe| {
8090 let cmp = probe
8091 .end
8092 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8093 if cmp.is_gt() {
8094 Ordering::Greater
8095 } else {
8096 Ordering::Less
8097 }
8098 }) {
8099 Ok(i) | Err(i) => i,
8100 };
8101 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8102 if let (Some(start_display), Some(end_display)) = (start, end) {
8103 results.push(
8104 start_display.to_display_point(display_snapshot)
8105 ..=end_display.to_display_point(display_snapshot),
8106 );
8107 }
8108 };
8109 let mut start_row: Option<Point> = None;
8110 let mut end_row: Option<Point> = None;
8111 if ranges.len() > count {
8112 return Vec::new();
8113 }
8114 for range in &ranges[start_ix..] {
8115 if range
8116 .start
8117 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8118 .is_ge()
8119 {
8120 break;
8121 }
8122 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8123 if let Some(current_row) = &end_row {
8124 if end.row == current_row.row {
8125 continue;
8126 }
8127 }
8128 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8129 if start_row.is_none() {
8130 assert_eq!(end_row, None);
8131 start_row = Some(start);
8132 end_row = Some(end);
8133 continue;
8134 }
8135 if let Some(current_end) = end_row.as_mut() {
8136 if start.row > current_end.row + 1 {
8137 push_region(start_row, end_row);
8138 start_row = Some(start);
8139 end_row = Some(end);
8140 } else {
8141 // Merge two hunks.
8142 *current_end = end;
8143 }
8144 } else {
8145 unreachable!();
8146 }
8147 }
8148 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8149 push_region(start_row, end_row);
8150 results
8151 }
8152
8153 pub fn highlight_text<T: 'static>(
8154 &mut self,
8155 ranges: Vec<Range<Anchor>>,
8156 style: HighlightStyle,
8157 cx: &mut ViewContext<Self>,
8158 ) {
8159 self.display_map.update(cx, |map, _| {
8160 map.highlight_text(TypeId::of::<T>(), ranges, style)
8161 });
8162 cx.notify();
8163 }
8164
8165 pub fn highlight_inlays<T: 'static>(
8166 &mut self,
8167 highlights: Vec<InlayHighlight>,
8168 style: HighlightStyle,
8169 cx: &mut ViewContext<Self>,
8170 ) {
8171 self.display_map.update(cx, |map, _| {
8172 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8173 });
8174 cx.notify();
8175 }
8176
8177 pub fn text_highlights<'a, T: 'static>(
8178 &'a self,
8179 cx: &'a AppContext,
8180 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8181 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8182 }
8183
8184 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8185 let cleared = self
8186 .display_map
8187 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8188 if cleared {
8189 cx.notify();
8190 }
8191 }
8192
8193 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8194 self.blink_manager.read(cx).visible() && self.focused
8195 }
8196
8197 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8198 cx.notify();
8199 }
8200
8201 fn on_buffer_event(
8202 &mut self,
8203 multibuffer: ModelHandle<MultiBuffer>,
8204 event: &multi_buffer::Event,
8205 cx: &mut ViewContext<Self>,
8206 ) {
8207 match event {
8208 multi_buffer::Event::Edited {
8209 sigleton_buffer_edited,
8210 } => {
8211 self.refresh_active_diagnostics(cx);
8212 self.refresh_code_actions(cx);
8213 if self.has_active_copilot_suggestion(cx) {
8214 self.update_visible_copilot_suggestion(cx);
8215 }
8216 cx.emit(Event::BufferEdited);
8217
8218 if *sigleton_buffer_edited {
8219 if let Some(project) = &self.project {
8220 let project = project.read(cx);
8221 let languages_affected = multibuffer
8222 .read(cx)
8223 .all_buffers()
8224 .into_iter()
8225 .filter_map(|buffer| {
8226 let buffer = buffer.read(cx);
8227 let language = buffer.language()?;
8228 if project.is_local()
8229 && project.language_servers_for_buffer(buffer, cx).count() == 0
8230 {
8231 None
8232 } else {
8233 Some(language)
8234 }
8235 })
8236 .cloned()
8237 .collect::<HashSet<_>>();
8238 if !languages_affected.is_empty() {
8239 self.refresh_inlay_hints(
8240 InlayHintRefreshReason::BufferEdited(languages_affected),
8241 cx,
8242 );
8243 }
8244 }
8245 }
8246 }
8247 multi_buffer::Event::ExcerptsAdded {
8248 buffer,
8249 predecessor,
8250 excerpts,
8251 } => {
8252 cx.emit(Event::ExcerptsAdded {
8253 buffer: buffer.clone(),
8254 predecessor: *predecessor,
8255 excerpts: excerpts.clone(),
8256 });
8257 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8258 }
8259 multi_buffer::Event::ExcerptsRemoved { ids } => {
8260 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8261 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8262 }
8263 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8264 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8265 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8266 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8267 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8268 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8269 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8270 multi_buffer::Event::DiagnosticsUpdated => {
8271 self.refresh_active_diagnostics(cx);
8272 }
8273 _ => {}
8274 };
8275 }
8276
8277 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8278 cx.notify();
8279 }
8280
8281 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8282 self.refresh_copilot_suggestions(true, cx);
8283 self.refresh_inlay_hints(
8284 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8285 self.selections.newest_anchor().head(),
8286 &self.buffer.read(cx).snapshot(cx),
8287 cx,
8288 )),
8289 cx,
8290 );
8291 }
8292
8293 pub fn set_searchable(&mut self, searchable: bool) {
8294 self.searchable = searchable;
8295 }
8296
8297 pub fn searchable(&self) -> bool {
8298 self.searchable
8299 }
8300
8301 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8302 let active_item = workspace.active_item(cx);
8303 let editor_handle = if let Some(editor) = active_item
8304 .as_ref()
8305 .and_then(|item| item.act_as::<Self>(cx))
8306 {
8307 editor
8308 } else {
8309 cx.propagate_action();
8310 return;
8311 };
8312
8313 let editor = editor_handle.read(cx);
8314 let buffer = editor.buffer.read(cx);
8315 if buffer.is_singleton() {
8316 cx.propagate_action();
8317 return;
8318 }
8319
8320 let mut new_selections_by_buffer = HashMap::default();
8321 for selection in editor.selections.all::<usize>(cx) {
8322 for (buffer, mut range, _) in
8323 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8324 {
8325 if selection.reversed {
8326 mem::swap(&mut range.start, &mut range.end);
8327 }
8328 new_selections_by_buffer
8329 .entry(buffer)
8330 .or_insert(Vec::new())
8331 .push(range)
8332 }
8333 }
8334
8335 editor_handle.update(cx, |editor, cx| {
8336 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8337 });
8338 let pane = workspace.active_pane().clone();
8339 pane.update(cx, |pane, _| pane.disable_history());
8340
8341 // We defer the pane interaction because we ourselves are a workspace item
8342 // and activating a new item causes the pane to call a method on us reentrantly,
8343 // which panics if we're on the stack.
8344 cx.defer(move |workspace, cx| {
8345 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8346 let editor = workspace.open_project_item::<Self>(buffer, cx);
8347 editor.update(cx, |editor, cx| {
8348 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8349 s.select_ranges(ranges);
8350 });
8351 });
8352 }
8353
8354 pane.update(cx, |pane, _| pane.enable_history());
8355 });
8356 }
8357
8358 fn jump(
8359 workspace: &mut Workspace,
8360 path: ProjectPath,
8361 position: Point,
8362 anchor: language::Anchor,
8363 cx: &mut ViewContext<Workspace>,
8364 ) {
8365 let editor = workspace.open_path(path, None, true, cx);
8366 cx.spawn(|_, mut cx| async move {
8367 let editor = editor
8368 .await?
8369 .downcast::<Editor>()
8370 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8371 .downgrade();
8372 editor.update(&mut cx, |editor, cx| {
8373 let buffer = editor
8374 .buffer()
8375 .read(cx)
8376 .as_singleton()
8377 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8378 let buffer = buffer.read(cx);
8379 let cursor = if buffer.can_resolve(&anchor) {
8380 language::ToPoint::to_point(&anchor, buffer)
8381 } else {
8382 buffer.clip_point(position, Bias::Left)
8383 };
8384
8385 let nav_history = editor.nav_history.take();
8386 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8387 s.select_ranges([cursor..cursor]);
8388 });
8389 editor.nav_history = nav_history;
8390
8391 anyhow::Ok(())
8392 })??;
8393
8394 anyhow::Ok(())
8395 })
8396 .detach_and_log_err(cx);
8397 }
8398
8399 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8400 let snapshot = self.buffer.read(cx).read(cx);
8401 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8402 Some(
8403 ranges
8404 .iter()
8405 .map(move |range| {
8406 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8407 })
8408 .collect(),
8409 )
8410 }
8411
8412 fn selection_replacement_ranges(
8413 &self,
8414 range: Range<OffsetUtf16>,
8415 cx: &AppContext,
8416 ) -> Vec<Range<OffsetUtf16>> {
8417 let selections = self.selections.all::<OffsetUtf16>(cx);
8418 let newest_selection = selections
8419 .iter()
8420 .max_by_key(|selection| selection.id)
8421 .unwrap();
8422 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8423 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8424 let snapshot = self.buffer.read(cx).read(cx);
8425 selections
8426 .into_iter()
8427 .map(|mut selection| {
8428 selection.start.0 =
8429 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8430 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8431 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8432 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8433 })
8434 .collect()
8435 }
8436
8437 fn report_copilot_event(
8438 &self,
8439 suggestion_id: Option<String>,
8440 suggestion_accepted: bool,
8441 cx: &AppContext,
8442 ) {
8443 let Some(project) = &self.project else { return };
8444
8445 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8446 let file_extension = self
8447 .buffer
8448 .read(cx)
8449 .as_singleton()
8450 .and_then(|b| b.read(cx).file())
8451 .and_then(|file| Path::new(file.file_name(cx)).extension())
8452 .and_then(|e| e.to_str())
8453 .map(|a| a.to_string());
8454
8455 let telemetry = project.read(cx).client().telemetry().clone();
8456 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8457
8458 let event = ClickhouseEvent::Copilot {
8459 suggestion_id,
8460 suggestion_accepted,
8461 file_extension,
8462 };
8463 telemetry.report_clickhouse_event(event, telemetry_settings);
8464 }
8465
8466 fn report_editor_event(
8467 &self,
8468 operation: &'static str,
8469 file_extension: Option<String>,
8470 cx: &AppContext,
8471 ) {
8472 let Some(project) = &self.project else { return };
8473
8474 // If None, we are in a file without an extension
8475 let file = self
8476 .buffer
8477 .read(cx)
8478 .as_singleton()
8479 .and_then(|b| b.read(cx).file());
8480 let file_extension = file_extension.or(file
8481 .as_ref()
8482 .and_then(|file| Path::new(file.file_name(cx)).extension())
8483 .and_then(|e| e.to_str())
8484 .map(|a| a.to_string()));
8485
8486 let vim_mode = cx
8487 .global::<SettingsStore>()
8488 .raw_user_settings()
8489 .get("vim_mode")
8490 == Some(&serde_json::Value::Bool(true));
8491 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8492 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8493 let copilot_enabled_for_language = self
8494 .buffer
8495 .read(cx)
8496 .settings_at(0, cx)
8497 .show_copilot_suggestions;
8498
8499 let telemetry = project.read(cx).client().telemetry().clone();
8500 let event = ClickhouseEvent::Editor {
8501 file_extension,
8502 vim_mode,
8503 operation,
8504 copilot_enabled,
8505 copilot_enabled_for_language,
8506 };
8507 telemetry.report_clickhouse_event(event, telemetry_settings)
8508 }
8509
8510 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8511 /// with each line being an array of {text, highlight} objects.
8512 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8513 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8514 return;
8515 };
8516
8517 #[derive(Serialize)]
8518 struct Chunk<'a> {
8519 text: String,
8520 highlight: Option<&'a str>,
8521 }
8522
8523 let snapshot = buffer.read(cx).snapshot();
8524 let range = self
8525 .selected_text_range(cx)
8526 .and_then(|selected_range| {
8527 if selected_range.is_empty() {
8528 None
8529 } else {
8530 Some(selected_range)
8531 }
8532 })
8533 .unwrap_or_else(|| 0..snapshot.len());
8534
8535 let chunks = snapshot.chunks(range, true);
8536 let mut lines = Vec::new();
8537 let mut line: VecDeque<Chunk> = VecDeque::new();
8538
8539 let theme = &theme::current(cx).editor.syntax;
8540
8541 for chunk in chunks {
8542 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8543 let mut chunk_lines = chunk.text.split("\n").peekable();
8544 while let Some(text) = chunk_lines.next() {
8545 let mut merged_with_last_token = false;
8546 if let Some(last_token) = line.back_mut() {
8547 if last_token.highlight == highlight {
8548 last_token.text.push_str(text);
8549 merged_with_last_token = true;
8550 }
8551 }
8552
8553 if !merged_with_last_token {
8554 line.push_back(Chunk {
8555 text: text.into(),
8556 highlight,
8557 });
8558 }
8559
8560 if chunk_lines.peek().is_some() {
8561 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8562 line.pop_front();
8563 }
8564 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8565 line.pop_back();
8566 }
8567
8568 lines.push(mem::take(&mut line));
8569 }
8570 }
8571 }
8572
8573 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8574 return;
8575 };
8576 cx.write_to_clipboard(ClipboardItem::new(lines));
8577 }
8578
8579 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8580 &self.inlay_hint_cache
8581 }
8582
8583 pub fn replay_insert_event(
8584 &mut self,
8585 text: &str,
8586 relative_utf16_range: Option<Range<isize>>,
8587 cx: &mut ViewContext<Self>,
8588 ) {
8589 if !self.input_enabled {
8590 cx.emit(Event::InputIgnored { text: text.into() });
8591 return;
8592 }
8593 if let Some(relative_utf16_range) = relative_utf16_range {
8594 let selections = self.selections.all::<OffsetUtf16>(cx);
8595 self.change_selections(None, cx, |s| {
8596 let new_ranges = selections.into_iter().map(|range| {
8597 let start = OffsetUtf16(
8598 range
8599 .head()
8600 .0
8601 .saturating_add_signed(relative_utf16_range.start),
8602 );
8603 let end = OffsetUtf16(
8604 range
8605 .head()
8606 .0
8607 .saturating_add_signed(relative_utf16_range.end),
8608 );
8609 start..end
8610 });
8611 s.select_ranges(new_ranges);
8612 });
8613 }
8614
8615 self.handle_input(text, cx);
8616 }
8617
8618 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
8619 let Some(project) = self.project.as_ref() else {
8620 return false;
8621 };
8622 let project = project.read(cx);
8623
8624 let mut supports = false;
8625 self.buffer().read(cx).for_each_buffer(|buffer| {
8626 if !supports {
8627 supports = project
8628 .language_servers_for_buffer(buffer.read(cx), cx)
8629 .any(
8630 |(_, server)| match server.capabilities().inlay_hint_provider {
8631 Some(lsp::OneOf::Left(enabled)) => enabled,
8632 Some(lsp::OneOf::Right(_)) => true,
8633 None => false,
8634 },
8635 )
8636 }
8637 });
8638 supports
8639 }
8640}
8641
8642pub trait CollaborationHub {
8643 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
8644 fn user_participant_indices<'a>(
8645 &self,
8646 cx: &'a AppContext,
8647 ) -> &'a HashMap<u64, ParticipantIndex>;
8648}
8649
8650impl CollaborationHub for ModelHandle<Project> {
8651 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
8652 self.read(cx).collaborators()
8653 }
8654
8655 fn user_participant_indices<'a>(
8656 &self,
8657 cx: &'a AppContext,
8658 ) -> &'a HashMap<u64, ParticipantIndex> {
8659 self.read(cx).user_store().read(cx).participant_indices()
8660 }
8661}
8662
8663fn inlay_hint_settings(
8664 location: Anchor,
8665 snapshot: &MultiBufferSnapshot,
8666 cx: &mut ViewContext<'_, '_, Editor>,
8667) -> InlayHintSettings {
8668 let file = snapshot.file_at(location);
8669 let language = snapshot.language_at(location);
8670 let settings = all_language_settings(file, cx);
8671 settings
8672 .language(language.map(|l| l.name()).as_deref())
8673 .inlay_hints
8674}
8675
8676fn consume_contiguous_rows(
8677 contiguous_row_selections: &mut Vec<Selection<Point>>,
8678 selection: &Selection<Point>,
8679 display_map: &DisplaySnapshot,
8680 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8681) -> (u32, u32) {
8682 contiguous_row_selections.push(selection.clone());
8683 let start_row = selection.start.row;
8684 let mut end_row = ending_row(selection, display_map);
8685
8686 while let Some(next_selection) = selections.peek() {
8687 if next_selection.start.row <= end_row {
8688 end_row = ending_row(next_selection, display_map);
8689 contiguous_row_selections.push(selections.next().unwrap().clone());
8690 } else {
8691 break;
8692 }
8693 }
8694 (start_row, end_row)
8695}
8696
8697fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8698 if next_selection.end.column > 0 || next_selection.is_empty() {
8699 display_map.next_line_boundary(next_selection.end).0.row + 1
8700 } else {
8701 next_selection.end.row
8702 }
8703}
8704
8705impl EditorSnapshot {
8706 pub fn remote_selections_in_range<'a>(
8707 &'a self,
8708 range: &'a Range<Anchor>,
8709 collaboration_hub: &dyn CollaborationHub,
8710 cx: &'a AppContext,
8711 ) -> impl 'a + Iterator<Item = RemoteSelection> {
8712 let participant_indices = collaboration_hub.user_participant_indices(cx);
8713 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
8714 let collaborators_by_replica_id = collaborators_by_peer_id
8715 .iter()
8716 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
8717 .collect::<HashMap<_, _>>();
8718 self.buffer_snapshot
8719 .remote_selections_in_range(range)
8720 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
8721 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
8722 let participant_index = participant_indices.get(&collaborator.user_id).copied();
8723 Some(RemoteSelection {
8724 replica_id,
8725 selection,
8726 cursor_shape,
8727 line_mode,
8728 participant_index,
8729 peer_id: collaborator.peer_id,
8730 })
8731 })
8732 }
8733
8734 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8735 self.display_snapshot.buffer_snapshot.language_at(position)
8736 }
8737
8738 pub fn is_focused(&self) -> bool {
8739 self.is_focused
8740 }
8741
8742 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8743 self.placeholder_text.as_ref()
8744 }
8745
8746 pub fn scroll_position(&self) -> Vector2F {
8747 self.scroll_anchor.scroll_position(&self.display_snapshot)
8748 }
8749}
8750
8751impl Deref for EditorSnapshot {
8752 type Target = DisplaySnapshot;
8753
8754 fn deref(&self) -> &Self::Target {
8755 &self.display_snapshot
8756 }
8757}
8758
8759#[derive(Clone, Debug, PartialEq, Eq)]
8760pub enum Event {
8761 InputIgnored {
8762 text: Arc<str>,
8763 },
8764 InputHandled {
8765 utf16_range_to_replace: Option<Range<isize>>,
8766 text: Arc<str>,
8767 },
8768 ExcerptsAdded {
8769 buffer: ModelHandle<Buffer>,
8770 predecessor: ExcerptId,
8771 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8772 },
8773 ExcerptsRemoved {
8774 ids: Vec<ExcerptId>,
8775 },
8776 BufferEdited,
8777 Edited,
8778 Reparsed,
8779 Focused,
8780 Blurred,
8781 DirtyChanged,
8782 Saved,
8783 TitleChanged,
8784 DiffBaseChanged,
8785 SelectionsChanged {
8786 local: bool,
8787 },
8788 ScrollPositionChanged {
8789 local: bool,
8790 autoscroll: bool,
8791 },
8792 Closed,
8793}
8794
8795pub struct EditorFocused(pub ViewHandle<Editor>);
8796pub struct EditorBlurred(pub ViewHandle<Editor>);
8797pub struct EditorReleased(pub WeakViewHandle<Editor>);
8798
8799impl Entity for Editor {
8800 type Event = Event;
8801
8802 fn release(&mut self, cx: &mut AppContext) {
8803 cx.emit_global(EditorReleased(self.handle.clone()));
8804 }
8805}
8806
8807impl View for Editor {
8808 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8809 let style = self.style(cx);
8810 let font_changed = self.display_map.update(cx, |map, cx| {
8811 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8812 map.set_font(style.text.font_id, style.text.font_size, cx)
8813 });
8814
8815 if font_changed {
8816 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8817 hide_hover(editor, cx);
8818 hide_link_definition(editor, cx);
8819 });
8820 }
8821
8822 Stack::new()
8823 .with_child(EditorElement::new(style.clone()))
8824 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8825 .into_any()
8826 }
8827
8828 fn ui_name() -> &'static str {
8829 "Editor"
8830 }
8831
8832 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
8833 if cx.is_self_focused() {
8834 let focused_event = EditorFocused(cx.handle());
8835 cx.emit(Event::Focused);
8836 cx.emit_global(focused_event);
8837 }
8838 if let Some(rename) = self.pending_rename.as_ref() {
8839 cx.focus(&rename.editor);
8840 } else if cx.is_self_focused() || !focused.is::<Editor>() {
8841 if !self.focused {
8842 self.blink_manager.update(cx, BlinkManager::enable);
8843 }
8844 self.focused = true;
8845 self.buffer.update(cx, |buffer, cx| {
8846 buffer.finalize_last_transaction(cx);
8847 if self.leader_peer_id.is_none() {
8848 buffer.set_active_selections(
8849 &self.selections.disjoint_anchors(),
8850 self.selections.line_mode,
8851 self.cursor_shape,
8852 cx,
8853 );
8854 }
8855 });
8856 }
8857 }
8858
8859 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8860 let blurred_event = EditorBlurred(cx.handle());
8861 cx.emit_global(blurred_event);
8862 self.focused = false;
8863 self.blink_manager.update(cx, BlinkManager::disable);
8864 self.buffer
8865 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8866 self.hide_context_menu(cx);
8867 hide_hover(self, cx);
8868 cx.emit(Event::Blurred);
8869 cx.notify();
8870 }
8871
8872 fn modifiers_changed(
8873 &mut self,
8874 event: &gpui::platform::ModifiersChangedEvent,
8875 cx: &mut ViewContext<Self>,
8876 ) -> bool {
8877 let pending_selection = self.has_pending_selection();
8878
8879 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
8880 if event.cmd && !pending_selection {
8881 let point = point.clone();
8882 let snapshot = self.snapshot(cx);
8883 let kind = point.definition_kind(event.shift);
8884
8885 show_link_definition(kind, self, point, snapshot, cx);
8886 return false;
8887 }
8888 }
8889
8890 {
8891 if self.link_go_to_definition_state.symbol_range.is_some()
8892 || !self.link_go_to_definition_state.definitions.is_empty()
8893 {
8894 self.link_go_to_definition_state.symbol_range.take();
8895 self.link_go_to_definition_state.definitions.clear();
8896 cx.notify();
8897 }
8898
8899 self.link_go_to_definition_state.task = None;
8900
8901 self.clear_highlights::<LinkGoToDefinitionState>(cx);
8902 }
8903
8904 false
8905 }
8906
8907 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8908 Self::reset_to_default_keymap_context(keymap);
8909 let mode = match self.mode {
8910 EditorMode::SingleLine => "single_line",
8911 EditorMode::AutoHeight { .. } => "auto_height",
8912 EditorMode::Full => "full",
8913 };
8914 keymap.add_key("mode", mode);
8915 if self.pending_rename.is_some() {
8916 keymap.add_identifier("renaming");
8917 }
8918 if self.context_menu_visible() {
8919 match self.context_menu.as_ref() {
8920 Some(ContextMenu::Completions(_)) => {
8921 keymap.add_identifier("menu");
8922 keymap.add_identifier("showing_completions")
8923 }
8924 Some(ContextMenu::CodeActions(_)) => {
8925 keymap.add_identifier("menu");
8926 keymap.add_identifier("showing_code_actions")
8927 }
8928 None => {}
8929 }
8930 }
8931
8932 for layer in self.keymap_context_layers.values() {
8933 keymap.extend(layer);
8934 }
8935
8936 if let Some(extension) = self
8937 .buffer
8938 .read(cx)
8939 .as_singleton()
8940 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8941 {
8942 keymap.add_key("extension", extension.to_string());
8943 }
8944 }
8945
8946 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8947 Some(
8948 self.buffer
8949 .read(cx)
8950 .read(cx)
8951 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8952 .collect(),
8953 )
8954 }
8955
8956 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8957 // Prevent the IME menu from appearing when holding down an alphabetic key
8958 // while input is disabled.
8959 if !self.input_enabled {
8960 return None;
8961 }
8962
8963 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8964 Some(range.start.0..range.end.0)
8965 }
8966
8967 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8968 let snapshot = self.buffer.read(cx).read(cx);
8969 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8970 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8971 }
8972
8973 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8974 self.clear_highlights::<InputComposition>(cx);
8975 self.ime_transaction.take();
8976 }
8977
8978 fn replace_text_in_range(
8979 &mut self,
8980 range_utf16: Option<Range<usize>>,
8981 text: &str,
8982 cx: &mut ViewContext<Self>,
8983 ) {
8984 if !self.input_enabled {
8985 cx.emit(Event::InputIgnored { text: text.into() });
8986 return;
8987 }
8988
8989 self.transact(cx, |this, cx| {
8990 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8991 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8992 Some(this.selection_replacement_ranges(range_utf16, cx))
8993 } else {
8994 this.marked_text_ranges(cx)
8995 };
8996
8997 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
8998 let newest_selection_id = this.selections.newest_anchor().id;
8999 this.selections
9000 .all::<OffsetUtf16>(cx)
9001 .iter()
9002 .zip(ranges_to_replace.iter())
9003 .find_map(|(selection, range)| {
9004 if selection.id == newest_selection_id {
9005 Some(
9006 (range.start.0 as isize - selection.head().0 as isize)
9007 ..(range.end.0 as isize - selection.head().0 as isize),
9008 )
9009 } else {
9010 None
9011 }
9012 })
9013 });
9014
9015 cx.emit(Event::InputHandled {
9016 utf16_range_to_replace: range_to_replace,
9017 text: text.into(),
9018 });
9019
9020 if let Some(new_selected_ranges) = new_selected_ranges {
9021 this.change_selections(None, cx, |selections| {
9022 selections.select_ranges(new_selected_ranges)
9023 });
9024 }
9025
9026 this.handle_input(text, cx);
9027 });
9028
9029 if let Some(transaction) = self.ime_transaction {
9030 self.buffer.update(cx, |buffer, cx| {
9031 buffer.group_until_transaction(transaction, cx);
9032 });
9033 }
9034
9035 self.unmark_text(cx);
9036 }
9037
9038 fn replace_and_mark_text_in_range(
9039 &mut self,
9040 range_utf16: Option<Range<usize>>,
9041 text: &str,
9042 new_selected_range_utf16: Option<Range<usize>>,
9043 cx: &mut ViewContext<Self>,
9044 ) {
9045 if !self.input_enabled {
9046 cx.emit(Event::InputIgnored { text: text.into() });
9047 return;
9048 }
9049
9050 let transaction = self.transact(cx, |this, cx| {
9051 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9052 let snapshot = this.buffer.read(cx).read(cx);
9053 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9054 for marked_range in &mut marked_ranges {
9055 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9056 marked_range.start.0 += relative_range_utf16.start;
9057 marked_range.start =
9058 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9059 marked_range.end =
9060 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9061 }
9062 }
9063 Some(marked_ranges)
9064 } else if let Some(range_utf16) = range_utf16 {
9065 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9066 Some(this.selection_replacement_ranges(range_utf16, cx))
9067 } else {
9068 None
9069 };
9070
9071 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9072 let newest_selection_id = this.selections.newest_anchor().id;
9073 this.selections
9074 .all::<OffsetUtf16>(cx)
9075 .iter()
9076 .zip(ranges_to_replace.iter())
9077 .find_map(|(selection, range)| {
9078 if selection.id == newest_selection_id {
9079 Some(
9080 (range.start.0 as isize - selection.head().0 as isize)
9081 ..(range.end.0 as isize - selection.head().0 as isize),
9082 )
9083 } else {
9084 None
9085 }
9086 })
9087 });
9088
9089 cx.emit(Event::InputHandled {
9090 utf16_range_to_replace: range_to_replace,
9091 text: text.into(),
9092 });
9093
9094 if let Some(ranges) = ranges_to_replace {
9095 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9096 }
9097
9098 let marked_ranges = {
9099 let snapshot = this.buffer.read(cx).read(cx);
9100 this.selections
9101 .disjoint_anchors()
9102 .iter()
9103 .map(|selection| {
9104 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9105 })
9106 .collect::<Vec<_>>()
9107 };
9108
9109 if text.is_empty() {
9110 this.unmark_text(cx);
9111 } else {
9112 this.highlight_text::<InputComposition>(
9113 marked_ranges.clone(),
9114 this.style(cx).composition_mark,
9115 cx,
9116 );
9117 }
9118
9119 this.handle_input(text, cx);
9120
9121 if let Some(new_selected_range) = new_selected_range_utf16 {
9122 let snapshot = this.buffer.read(cx).read(cx);
9123 let new_selected_ranges = marked_ranges
9124 .into_iter()
9125 .map(|marked_range| {
9126 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9127 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9128 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9129 snapshot.clip_offset_utf16(new_start, Bias::Left)
9130 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9131 })
9132 .collect::<Vec<_>>();
9133
9134 drop(snapshot);
9135 this.change_selections(None, cx, |selections| {
9136 selections.select_ranges(new_selected_ranges)
9137 });
9138 }
9139 });
9140
9141 self.ime_transaction = self.ime_transaction.or(transaction);
9142 if let Some(transaction) = self.ime_transaction {
9143 self.buffer.update(cx, |buffer, cx| {
9144 buffer.group_until_transaction(transaction, cx);
9145 });
9146 }
9147
9148 if self.text_highlights::<InputComposition>(cx).is_none() {
9149 self.ime_transaction.take();
9150 }
9151 }
9152}
9153
9154fn build_style(
9155 settings: &ThemeSettings,
9156 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9157 override_text_style: Option<&OverrideTextStyle>,
9158 cx: &AppContext,
9159) -> EditorStyle {
9160 let font_cache = cx.font_cache();
9161 let line_height_scalar = settings.line_height();
9162 let theme_id = settings.theme.meta.id;
9163 let mut theme = settings.theme.editor.clone();
9164 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9165 let field_editor_theme = get_field_editor_theme(&settings.theme);
9166 theme.text_color = field_editor_theme.text.color;
9167 theme.selection = field_editor_theme.selection;
9168 theme.background = field_editor_theme
9169 .container
9170 .background_color
9171 .unwrap_or_default();
9172 EditorStyle {
9173 text: field_editor_theme.text,
9174 placeholder_text: field_editor_theme.placeholder_text,
9175 line_height_scalar,
9176 theme,
9177 theme_id,
9178 }
9179 } else {
9180 let font_family_id = settings.buffer_font_family;
9181 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9182 let font_properties = Default::default();
9183 let font_id = font_cache
9184 .select_font(font_family_id, &font_properties)
9185 .unwrap();
9186 let font_size = settings.buffer_font_size(cx);
9187 EditorStyle {
9188 text: TextStyle {
9189 color: settings.theme.editor.text_color,
9190 font_family_name,
9191 font_family_id,
9192 font_id,
9193 font_size,
9194 font_properties,
9195 underline: Default::default(),
9196 soft_wrap: false,
9197 },
9198 placeholder_text: None,
9199 line_height_scalar,
9200 theme,
9201 theme_id,
9202 }
9203 };
9204
9205 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9206 if let Some(highlighted) = style
9207 .text
9208 .clone()
9209 .highlight(highlight_style, font_cache)
9210 .log_err()
9211 {
9212 style.text = highlighted;
9213 }
9214 }
9215
9216 style
9217}
9218
9219trait SelectionExt {
9220 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9221 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9222 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9223 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9224 -> Range<u32>;
9225}
9226
9227impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9228 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9229 let start = self.start.to_point(buffer);
9230 let end = self.end.to_point(buffer);
9231 if self.reversed {
9232 end..start
9233 } else {
9234 start..end
9235 }
9236 }
9237
9238 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9239 let start = self.start.to_offset(buffer);
9240 let end = self.end.to_offset(buffer);
9241 if self.reversed {
9242 end..start
9243 } else {
9244 start..end
9245 }
9246 }
9247
9248 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9249 let start = self
9250 .start
9251 .to_point(&map.buffer_snapshot)
9252 .to_display_point(map);
9253 let end = self
9254 .end
9255 .to_point(&map.buffer_snapshot)
9256 .to_display_point(map);
9257 if self.reversed {
9258 end..start
9259 } else {
9260 start..end
9261 }
9262 }
9263
9264 fn spanned_rows(
9265 &self,
9266 include_end_if_at_line_start: bool,
9267 map: &DisplaySnapshot,
9268 ) -> Range<u32> {
9269 let start = self.start.to_point(&map.buffer_snapshot);
9270 let mut end = self.end.to_point(&map.buffer_snapshot);
9271 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9272 end.row -= 1;
9273 }
9274
9275 let buffer_start = map.prev_line_boundary(start).0;
9276 let buffer_end = map.next_line_boundary(end).0;
9277 buffer_start.row..buffer_end.row + 1
9278 }
9279}
9280
9281impl<T: InvalidationRegion> InvalidationStack<T> {
9282 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9283 where
9284 S: Clone + ToOffset,
9285 {
9286 while let Some(region) = self.last() {
9287 let all_selections_inside_invalidation_ranges =
9288 if selections.len() == region.ranges().len() {
9289 selections
9290 .iter()
9291 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9292 .all(|(selection, invalidation_range)| {
9293 let head = selection.head().to_offset(buffer);
9294 invalidation_range.start <= head && invalidation_range.end >= head
9295 })
9296 } else {
9297 false
9298 };
9299
9300 if all_selections_inside_invalidation_ranges {
9301 break;
9302 } else {
9303 self.pop();
9304 }
9305 }
9306 }
9307}
9308
9309impl<T> Default for InvalidationStack<T> {
9310 fn default() -> Self {
9311 Self(Default::default())
9312 }
9313}
9314
9315impl<T> Deref for InvalidationStack<T> {
9316 type Target = Vec<T>;
9317
9318 fn deref(&self) -> &Self::Target {
9319 &self.0
9320 }
9321}
9322
9323impl<T> DerefMut for InvalidationStack<T> {
9324 fn deref_mut(&mut self) -> &mut Self::Target {
9325 &mut self.0
9326 }
9327}
9328
9329impl InvalidationRegion for SnippetState {
9330 fn ranges(&self) -> &[Range<Anchor>] {
9331 &self.ranges[self.active_index]
9332 }
9333}
9334
9335impl Deref for EditorStyle {
9336 type Target = theme::Editor;
9337
9338 fn deref(&self) -> &Self::Target {
9339 &self.theme
9340 }
9341}
9342
9343pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9344 let mut highlighted_lines = Vec::new();
9345
9346 for (index, line) in diagnostic.message.lines().enumerate() {
9347 let line = match &diagnostic.source {
9348 Some(source) if index == 0 => {
9349 let source_highlight = Vec::from_iter(0..source.len());
9350 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9351 }
9352
9353 _ => highlight_diagnostic_message(Vec::new(), line),
9354 };
9355 highlighted_lines.push(line);
9356 }
9357 let message = diagnostic.message;
9358 Arc::new(move |cx: &mut BlockContext| {
9359 let message = message.clone();
9360 let settings = settings::get::<ThemeSettings>(cx);
9361 let tooltip_style = settings.theme.tooltip.clone();
9362 let theme = &settings.theme.editor;
9363 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9364 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9365 let anchor_x = cx.anchor_x;
9366 enum BlockContextToolip {}
9367 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9368 Flex::column()
9369 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9370 Label::new(
9371 line.clone(),
9372 style.message.clone().with_font_size(font_size),
9373 )
9374 .with_highlights(highlights.clone())
9375 .contained()
9376 .with_margin_left(anchor_x)
9377 }))
9378 .aligned()
9379 .left()
9380 .into_any()
9381 })
9382 .with_cursor_style(CursorStyle::PointingHand)
9383 .on_click(MouseButton::Left, move |_, _, cx| {
9384 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9385 })
9386 // We really need to rethink this ID system...
9387 .with_tooltip::<BlockContextToolip>(
9388 cx.block_id,
9389 "Copy diagnostic message",
9390 None,
9391 tooltip_style,
9392 cx,
9393 )
9394 .into_any()
9395 })
9396}
9397
9398pub fn highlight_diagnostic_message(
9399 initial_highlights: Vec<usize>,
9400 message: &str,
9401) -> (String, Vec<usize>) {
9402 let mut message_without_backticks = String::new();
9403 let mut prev_offset = 0;
9404 let mut inside_block = false;
9405 let mut highlights = initial_highlights;
9406 for (match_ix, (offset, _)) in message
9407 .match_indices('`')
9408 .chain([(message.len(), "")])
9409 .enumerate()
9410 {
9411 message_without_backticks.push_str(&message[prev_offset..offset]);
9412 if inside_block {
9413 highlights.extend(prev_offset - match_ix..offset - match_ix);
9414 }
9415
9416 inside_block = !inside_block;
9417 prev_offset = offset + 1;
9418 }
9419
9420 (message_without_backticks, highlights)
9421}
9422
9423pub fn diagnostic_style(
9424 severity: DiagnosticSeverity,
9425 valid: bool,
9426 theme: &theme::Editor,
9427) -> DiagnosticStyle {
9428 match (severity, valid) {
9429 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9430 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9431 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9432 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9433 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9434 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9435 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9436 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9437 _ => theme.invalid_hint_diagnostic.clone(),
9438 }
9439}
9440
9441pub fn combine_syntax_and_fuzzy_match_highlights(
9442 text: &str,
9443 default_style: HighlightStyle,
9444 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9445 match_indices: &[usize],
9446) -> Vec<(Range<usize>, HighlightStyle)> {
9447 let mut result = Vec::new();
9448 let mut match_indices = match_indices.iter().copied().peekable();
9449
9450 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9451 {
9452 syntax_highlight.weight = None;
9453
9454 // Add highlights for any fuzzy match characters before the next
9455 // syntax highlight range.
9456 while let Some(&match_index) = match_indices.peek() {
9457 if match_index >= range.start {
9458 break;
9459 }
9460 match_indices.next();
9461 let end_index = char_ix_after(match_index, text);
9462 let mut match_style = default_style;
9463 match_style.weight = Some(fonts::Weight::BOLD);
9464 result.push((match_index..end_index, match_style));
9465 }
9466
9467 if range.start == usize::MAX {
9468 break;
9469 }
9470
9471 // Add highlights for any fuzzy match characters within the
9472 // syntax highlight range.
9473 let mut offset = range.start;
9474 while let Some(&match_index) = match_indices.peek() {
9475 if match_index >= range.end {
9476 break;
9477 }
9478
9479 match_indices.next();
9480 if match_index > offset {
9481 result.push((offset..match_index, syntax_highlight));
9482 }
9483
9484 let mut end_index = char_ix_after(match_index, text);
9485 while let Some(&next_match_index) = match_indices.peek() {
9486 if next_match_index == end_index && next_match_index < range.end {
9487 end_index = char_ix_after(next_match_index, text);
9488 match_indices.next();
9489 } else {
9490 break;
9491 }
9492 }
9493
9494 let mut match_style = syntax_highlight;
9495 match_style.weight = Some(fonts::Weight::BOLD);
9496 result.push((match_index..end_index, match_style));
9497 offset = end_index;
9498 }
9499
9500 if offset < range.end {
9501 result.push((offset..range.end, syntax_highlight));
9502 }
9503 }
9504
9505 fn char_ix_after(ix: usize, text: &str) -> usize {
9506 ix + text[ix..].chars().next().unwrap().len_utf8()
9507 }
9508
9509 result
9510}
9511
9512pub fn styled_runs_for_code_label<'a>(
9513 label: &'a CodeLabel,
9514 syntax_theme: &'a theme::SyntaxTheme,
9515) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9516 let fade_out = HighlightStyle {
9517 fade_out: Some(0.35),
9518 ..Default::default()
9519 };
9520
9521 let mut prev_end = label.filter_range.end;
9522 label
9523 .runs
9524 .iter()
9525 .enumerate()
9526 .flat_map(move |(ix, (range, highlight_id))| {
9527 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9528 style
9529 } else {
9530 return Default::default();
9531 };
9532 let mut muted_style = style;
9533 muted_style.highlight(fade_out);
9534
9535 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9536 if range.start >= label.filter_range.end {
9537 if range.start > prev_end {
9538 runs.push((prev_end..range.start, fade_out));
9539 }
9540 runs.push((range.clone(), muted_style));
9541 } else if range.end <= label.filter_range.end {
9542 runs.push((range.clone(), style));
9543 } else {
9544 runs.push((range.start..label.filter_range.end, style));
9545 runs.push((label.filter_range.end..range.end, muted_style));
9546 }
9547 prev_end = cmp::max(prev_end, range.end);
9548
9549 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9550 runs.push((prev_end..label.text.len(), fade_out));
9551 }
9552
9553 runs
9554 })
9555}
9556
9557pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9558 let mut index = 0;
9559 let mut codepoints = text.char_indices().peekable();
9560
9561 std::iter::from_fn(move || {
9562 let start_index = index;
9563 while let Some((new_index, codepoint)) = codepoints.next() {
9564 index = new_index + codepoint.len_utf8();
9565 let current_upper = codepoint.is_uppercase();
9566 let next_upper = codepoints
9567 .peek()
9568 .map(|(_, c)| c.is_uppercase())
9569 .unwrap_or(false);
9570
9571 if !current_upper && next_upper {
9572 return Some(&text[start_index..index]);
9573 }
9574 }
9575
9576 index = text.len();
9577 if start_index < text.len() {
9578 return Some(&text[start_index..]);
9579 }
9580 None
9581 })
9582 .flat_map(|word| word.split_inclusive('_'))
9583 .flat_map(|word| word.split_inclusive('-'))
9584}
9585
9586trait RangeToAnchorExt {
9587 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9588}
9589
9590impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9591 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9592 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9593 }
9594}