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