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