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