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