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