1mod blink_manager;
2pub mod display_map;
3mod editor_settings;
4mod element;
5mod inlay_hint_cache;
6
7mod git;
8mod highlight_matching_bracket;
9mod hover_popover;
10pub mod items;
11mod link_go_to_definition;
12mod mouse_context_menu;
13pub mod movement;
14pub mod multi_buffer;
15mod persistence;
16pub mod scroll;
17pub mod selections_collection;
18
19#[cfg(test)]
20mod editor_tests;
21#[cfg(any(test, feature = "test-support"))]
22pub mod test;
23
24use ::git::diff::DiffHunk;
25use aho_corasick::AhoCorasick;
26use anyhow::{anyhow, Context, Result};
27use blink_manager::BlinkManager;
28use client::{ClickhouseEvent, Collaborator, ParticipantIndex, TelemetrySettings};
29use clock::{Global, ReplicaId};
30use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
31use convert_case::{Case, Casing};
32use copilot::Copilot;
33pub use display_map::DisplayPoint;
34use display_map::*;
35pub use editor_settings::EditorSettings;
36pub use element::{
37 Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
38};
39use futures::FutureExt;
40use fuzzy::{StringMatch, StringMatchCandidate};
41use gpui::{
42 actions,
43 color::Color,
44 elements::*,
45 executor,
46 fonts::{self, HighlightStyle, TextStyle},
47 geometry::vector::{vec2f, Vector2F},
48 impl_actions,
49 keymap_matcher::KeymapContext,
50 platform::{CursorStyle, MouseButton},
51 serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element,
52 Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
53 WindowContext,
54};
55use highlight_matching_bracket::refresh_matching_bracket_highlights;
56use hover_popover::{hide_hover, HoverState};
57use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
58pub use items::MAX_TAB_TITLE_LEN;
59use itertools::Itertools;
60pub use language::{char_kind, CharKind};
61use language::{
62 language_settings::{self, all_language_settings, InlayHintSettings},
63 point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion,
64 CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language,
65 LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal,
66 TransactionId,
67};
68use link_go_to_definition::{
69 hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
70 LinkGoToDefinitionState,
71};
72use log::error;
73use lsp::LanguageServerId;
74use 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 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3069 TextLayoutDetails {
3070 font_cache: cx.font_cache().clone(),
3071 text_layout_cache: cx.text_layout_cache().clone(),
3072 editor_style: self.style(cx),
3073 }
3074 }
3075
3076 fn splice_inlay_hints(
3077 &self,
3078 to_remove: Vec<InlayId>,
3079 to_insert: Vec<Inlay>,
3080 cx: &mut ViewContext<Self>,
3081 ) {
3082 self.display_map.update(cx, |display_map, cx| {
3083 display_map.splice_inlays(to_remove, to_insert, cx);
3084 });
3085 cx.notify();
3086 }
3087
3088 fn trigger_on_type_formatting(
3089 &self,
3090 input: String,
3091 cx: &mut ViewContext<Self>,
3092 ) -> Option<Task<Result<()>>> {
3093 if input.len() != 1 {
3094 return None;
3095 }
3096
3097 let project = self.project.as_ref()?;
3098 let position = self.selections.newest_anchor().head();
3099 let (buffer, buffer_position) = self
3100 .buffer
3101 .read(cx)
3102 .text_anchor_for_position(position.clone(), cx)?;
3103
3104 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3105 // hence we do LSP request & edit on host side only — add formats to host's history.
3106 let push_to_lsp_host_history = true;
3107 // If this is not the host, append its history with new edits.
3108 let push_to_client_history = project.read(cx).is_remote();
3109
3110 let on_type_formatting = project.update(cx, |project, cx| {
3111 project.on_type_format(
3112 buffer.clone(),
3113 buffer_position,
3114 input,
3115 push_to_lsp_host_history,
3116 cx,
3117 )
3118 });
3119 Some(cx.spawn(|editor, mut cx| async move {
3120 if let Some(transaction) = on_type_formatting.await? {
3121 if push_to_client_history {
3122 buffer.update(&mut cx, |buffer, _| {
3123 buffer.push_transaction(transaction, Instant::now());
3124 });
3125 }
3126 editor.update(&mut cx, |editor, cx| {
3127 editor.refresh_document_highlights(cx);
3128 })?;
3129 }
3130 Ok(())
3131 }))
3132 }
3133
3134 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
3135 if self.pending_rename.is_some() {
3136 return;
3137 }
3138
3139 let project = if let Some(project) = self.project.clone() {
3140 project
3141 } else {
3142 return;
3143 };
3144
3145 let position = self.selections.newest_anchor().head();
3146 let (buffer, buffer_position) = if let Some(output) = self
3147 .buffer
3148 .read(cx)
3149 .text_anchor_for_position(position.clone(), cx)
3150 {
3151 output
3152 } else {
3153 return;
3154 };
3155
3156 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
3157 let completions = project.update(cx, |project, cx| {
3158 project.completions(&buffer, buffer_position, cx)
3159 });
3160
3161 let id = post_inc(&mut self.next_completion_id);
3162 let project = self.project.clone();
3163 let task = cx.spawn(|this, mut cx| {
3164 async move {
3165 let menu = if let Some(completions) = completions.await.log_err() {
3166 let mut menu = CompletionsMenu {
3167 id,
3168 initial_position: position,
3169 match_candidates: completions
3170 .iter()
3171 .enumerate()
3172 .map(|(id, completion)| {
3173 StringMatchCandidate::new(
3174 id,
3175 completion.label.text[completion.label.filter_range.clone()]
3176 .into(),
3177 )
3178 })
3179 .collect(),
3180 buffer,
3181 project,
3182 completions: completions.into(),
3183 matches: Vec::new().into(),
3184 selected_item: 0,
3185 list: Default::default(),
3186 };
3187 menu.filter(query.as_deref(), cx.background()).await;
3188 if menu.matches.is_empty() {
3189 None
3190 } else {
3191 Some(menu)
3192 }
3193 } else {
3194 None
3195 };
3196
3197 this.update(&mut cx, |this, cx| {
3198 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
3199
3200 match this.context_menu.as_ref() {
3201 None => {}
3202 Some(ContextMenu::Completions(prev_menu)) => {
3203 if prev_menu.id > id {
3204 return;
3205 }
3206 }
3207 _ => return,
3208 }
3209
3210 if this.focused && menu.is_some() {
3211 let menu = menu.unwrap();
3212 this.show_context_menu(ContextMenu::Completions(menu), cx);
3213 } else if this.completion_tasks.is_empty() {
3214 // If there are no more completion tasks and the last menu was
3215 // empty, we should hide it. If it was already hidden, we should
3216 // also show the copilot suggestion when available.
3217 if this.hide_context_menu(cx).is_none() {
3218 this.update_visible_copilot_suggestion(cx);
3219 }
3220 }
3221 })?;
3222
3223 Ok::<_, anyhow::Error>(())
3224 }
3225 .log_err()
3226 });
3227 self.completion_tasks.push((id, task));
3228 }
3229
3230 pub fn confirm_completion(
3231 &mut self,
3232 action: &ConfirmCompletion,
3233 cx: &mut ViewContext<Self>,
3234 ) -> Option<Task<Result<()>>> {
3235 use language::ToOffset as _;
3236
3237 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3238 menu
3239 } else {
3240 return None;
3241 };
3242
3243 let mat = completions_menu
3244 .matches
3245 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3246 let buffer_handle = completions_menu.buffer;
3247 let completion = completions_menu.completions.get(mat.candidate_id)?;
3248
3249 let snippet;
3250 let text;
3251 if completion.is_snippet() {
3252 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3253 text = snippet.as_ref().unwrap().text.clone();
3254 } else {
3255 snippet = None;
3256 text = completion.new_text.clone();
3257 };
3258 let selections = self.selections.all::<usize>(cx);
3259 let buffer = buffer_handle.read(cx);
3260 let old_range = completion.old_range.to_offset(buffer);
3261 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3262
3263 let newest_selection = self.selections.newest_anchor();
3264 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3265 return None;
3266 }
3267
3268 let lookbehind = newest_selection
3269 .start
3270 .text_anchor
3271 .to_offset(buffer)
3272 .saturating_sub(old_range.start);
3273 let lookahead = old_range
3274 .end
3275 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3276 let mut common_prefix_len = old_text
3277 .bytes()
3278 .zip(text.bytes())
3279 .take_while(|(a, b)| a == b)
3280 .count();
3281
3282 let snapshot = self.buffer.read(cx).snapshot(cx);
3283 let mut range_to_replace: Option<Range<isize>> = None;
3284 let mut ranges = Vec::new();
3285 for selection in &selections {
3286 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3287 let start = selection.start.saturating_sub(lookbehind);
3288 let end = selection.end + lookahead;
3289 if selection.id == newest_selection.id {
3290 range_to_replace = Some(
3291 ((start + common_prefix_len) as isize - selection.start as isize)
3292 ..(end as isize - selection.start as isize),
3293 );
3294 }
3295 ranges.push(start + common_prefix_len..end);
3296 } else {
3297 common_prefix_len = 0;
3298 ranges.clear();
3299 ranges.extend(selections.iter().map(|s| {
3300 if s.id == newest_selection.id {
3301 range_to_replace = Some(
3302 old_range.start.to_offset_utf16(&snapshot).0 as isize
3303 - selection.start as isize
3304 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3305 - selection.start as isize,
3306 );
3307 old_range.clone()
3308 } else {
3309 s.start..s.end
3310 }
3311 }));
3312 break;
3313 }
3314 }
3315 let text = &text[common_prefix_len..];
3316
3317 cx.emit(Event::InputHandled {
3318 utf16_range_to_replace: range_to_replace,
3319 text: text.into(),
3320 });
3321
3322 self.transact(cx, |this, cx| {
3323 if let Some(mut snippet) = snippet {
3324 snippet.text = text.to_string();
3325 for tabstop in snippet.tabstops.iter_mut().flatten() {
3326 tabstop.start -= common_prefix_len as isize;
3327 tabstop.end -= common_prefix_len as isize;
3328 }
3329
3330 this.insert_snippet(&ranges, snippet, cx).log_err();
3331 } else {
3332 this.buffer.update(cx, |buffer, cx| {
3333 buffer.edit(
3334 ranges.iter().map(|range| (range.clone(), text)),
3335 this.autoindent_mode.clone(),
3336 cx,
3337 );
3338 });
3339 }
3340
3341 this.refresh_copilot_suggestions(true, cx);
3342 });
3343
3344 let project = self.project.clone()?;
3345 let apply_edits = project.update(cx, |project, cx| {
3346 project.apply_additional_edits_for_completion(
3347 buffer_handle,
3348 completion.clone(),
3349 true,
3350 cx,
3351 )
3352 });
3353 Some(cx.foreground().spawn(async move {
3354 apply_edits.await?;
3355 Ok(())
3356 }))
3357 }
3358
3359 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3360 if matches!(
3361 self.context_menu.as_ref(),
3362 Some(ContextMenu::CodeActions(_))
3363 ) {
3364 self.context_menu.take();
3365 cx.notify();
3366 return;
3367 }
3368
3369 let deployed_from_indicator = action.deployed_from_indicator;
3370 let mut task = self.code_actions_task.take();
3371 cx.spawn(|this, mut cx| async move {
3372 while let Some(prev_task) = task {
3373 prev_task.await;
3374 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3375 }
3376
3377 this.update(&mut cx, |this, cx| {
3378 if this.focused {
3379 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3380 this.show_context_menu(
3381 ContextMenu::CodeActions(CodeActionsMenu {
3382 buffer,
3383 actions,
3384 selected_item: Default::default(),
3385 list: Default::default(),
3386 deployed_from_indicator,
3387 }),
3388 cx,
3389 );
3390 }
3391 }
3392 })?;
3393
3394 Ok::<_, anyhow::Error>(())
3395 })
3396 .detach_and_log_err(cx);
3397 }
3398
3399 pub fn confirm_code_action(
3400 workspace: &mut Workspace,
3401 action: &ConfirmCodeAction,
3402 cx: &mut ViewContext<Workspace>,
3403 ) -> Option<Task<Result<()>>> {
3404 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3405 let actions_menu = if let ContextMenu::CodeActions(menu) =
3406 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3407 {
3408 menu
3409 } else {
3410 return None;
3411 };
3412 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3413 let action = actions_menu.actions.get(action_ix)?.clone();
3414 let title = action.lsp_action.title.clone();
3415 let buffer = actions_menu.buffer;
3416
3417 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3418 project.apply_code_action(buffer, action, true, cx)
3419 });
3420 let editor = editor.downgrade();
3421 Some(cx.spawn(|workspace, cx| async move {
3422 let project_transaction = apply_code_actions.await?;
3423 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3424 }))
3425 }
3426
3427 async fn open_project_transaction(
3428 this: &WeakViewHandle<Editor>,
3429 workspace: WeakViewHandle<Workspace>,
3430 transaction: ProjectTransaction,
3431 title: String,
3432 mut cx: AsyncAppContext,
3433 ) -> Result<()> {
3434 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3435
3436 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3437 entries.sort_unstable_by_key(|(buffer, _)| {
3438 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3439 });
3440
3441 // If the project transaction's edits are all contained within this editor, then
3442 // avoid opening a new editor to display them.
3443
3444 if let Some((buffer, transaction)) = entries.first() {
3445 if entries.len() == 1 {
3446 let excerpt = this.read_with(&cx, |editor, cx| {
3447 editor
3448 .buffer()
3449 .read(cx)
3450 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3451 })?;
3452 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3453 if excerpted_buffer == *buffer {
3454 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3455 let excerpt_range = excerpt_range.to_offset(buffer);
3456 buffer
3457 .edited_ranges_for_transaction::<usize>(transaction)
3458 .all(|range| {
3459 excerpt_range.start <= range.start
3460 && excerpt_range.end >= range.end
3461 })
3462 });
3463
3464 if all_edits_within_excerpt {
3465 return Ok(());
3466 }
3467 }
3468 }
3469 }
3470 } else {
3471 return Ok(());
3472 }
3473
3474 let mut ranges_to_highlight = Vec::new();
3475 let excerpt_buffer = cx.add_model(|cx| {
3476 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3477 for (buffer_handle, transaction) in &entries {
3478 let buffer = buffer_handle.read(cx);
3479 ranges_to_highlight.extend(
3480 multibuffer.push_excerpts_with_context_lines(
3481 buffer_handle.clone(),
3482 buffer
3483 .edited_ranges_for_transaction::<usize>(transaction)
3484 .collect(),
3485 1,
3486 cx,
3487 ),
3488 );
3489 }
3490 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3491 multibuffer
3492 });
3493
3494 workspace.update(&mut cx, |workspace, cx| {
3495 let project = workspace.project().clone();
3496 let editor =
3497 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3498 workspace.add_item(Box::new(editor.clone()), cx);
3499 editor.update(cx, |editor, cx| {
3500 editor.highlight_background::<Self>(
3501 ranges_to_highlight,
3502 |theme| theme.editor.highlighted_line_background,
3503 cx,
3504 );
3505 });
3506 })?;
3507
3508 Ok(())
3509 }
3510
3511 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3512 let project = self.project.clone()?;
3513 let buffer = self.buffer.read(cx);
3514 let newest_selection = self.selections.newest_anchor().clone();
3515 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3516 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3517 if start_buffer != end_buffer {
3518 return None;
3519 }
3520
3521 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3522 cx.background().timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT).await;
3523
3524 let actions = project
3525 .update(&mut cx, |project, cx| {
3526 project.code_actions(&start_buffer, start..end, cx)
3527 })
3528 .await;
3529
3530 this.update(&mut cx, |this, cx| {
3531 this.available_code_actions = actions.log_err().and_then(|actions| {
3532 if actions.is_empty() {
3533 None
3534 } else {
3535 Some((start_buffer, actions.into()))
3536 }
3537 });
3538 cx.notify();
3539 })
3540 .log_err();
3541 }));
3542 None
3543 }
3544
3545 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3546 if self.pending_rename.is_some() {
3547 return None;
3548 }
3549
3550 let project = self.project.clone()?;
3551 let buffer = self.buffer.read(cx);
3552 let newest_selection = self.selections.newest_anchor().clone();
3553 let cursor_position = newest_selection.head();
3554 let (cursor_buffer, cursor_buffer_position) =
3555 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3556 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3557 if cursor_buffer != tail_buffer {
3558 return None;
3559 }
3560
3561 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3562 cx.background()
3563 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
3564 .await;
3565
3566 let highlights = project
3567 .update(&mut cx, |project, cx| {
3568 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3569 })
3570 .await
3571 .log_err();
3572
3573 if let Some(highlights) = highlights {
3574 this.update(&mut cx, |this, cx| {
3575 if this.pending_rename.is_some() {
3576 return;
3577 }
3578
3579 let buffer_id = cursor_position.buffer_id;
3580 let buffer = this.buffer.read(cx);
3581 if !buffer
3582 .text_anchor_for_position(cursor_position, cx)
3583 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3584 {
3585 return;
3586 }
3587
3588 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3589 let mut write_ranges = Vec::new();
3590 let mut read_ranges = Vec::new();
3591 for highlight in highlights {
3592 for (excerpt_id, excerpt_range) in
3593 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3594 {
3595 let start = highlight
3596 .range
3597 .start
3598 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3599 let end = highlight
3600 .range
3601 .end
3602 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3603 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3604 continue;
3605 }
3606
3607 let range = Anchor {
3608 buffer_id,
3609 excerpt_id: excerpt_id.clone(),
3610 text_anchor: start,
3611 }..Anchor {
3612 buffer_id,
3613 excerpt_id,
3614 text_anchor: end,
3615 };
3616 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3617 write_ranges.push(range);
3618 } else {
3619 read_ranges.push(range);
3620 }
3621 }
3622 }
3623
3624 this.highlight_background::<DocumentHighlightRead>(
3625 read_ranges,
3626 |theme| theme.editor.document_highlight_read_background,
3627 cx,
3628 );
3629 this.highlight_background::<DocumentHighlightWrite>(
3630 write_ranges,
3631 |theme| theme.editor.document_highlight_write_background,
3632 cx,
3633 );
3634 cx.notify();
3635 })
3636 .log_err();
3637 }
3638 }));
3639 None
3640 }
3641
3642 fn refresh_copilot_suggestions(
3643 &mut self,
3644 debounce: bool,
3645 cx: &mut ViewContext<Self>,
3646 ) -> Option<()> {
3647 let copilot = Copilot::global(cx)?;
3648 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3649 self.clear_copilot_suggestions(cx);
3650 return None;
3651 }
3652 self.update_visible_copilot_suggestion(cx);
3653
3654 let snapshot = self.buffer.read(cx).snapshot(cx);
3655 let cursor = self.selections.newest_anchor().head();
3656 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3657 self.clear_copilot_suggestions(cx);
3658 return None;
3659 }
3660
3661 let (buffer, buffer_position) =
3662 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3663 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3664 if debounce {
3665 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3666 }
3667
3668 let completions = copilot
3669 .update(&mut cx, |copilot, cx| {
3670 copilot.completions(&buffer, buffer_position, cx)
3671 })
3672 .await
3673 .log_err()
3674 .into_iter()
3675 .flatten()
3676 .collect_vec();
3677
3678 this.update(&mut cx, |this, cx| {
3679 if !completions.is_empty() {
3680 this.copilot_state.cycled = false;
3681 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3682 this.copilot_state.completions.clear();
3683 this.copilot_state.active_completion_index = 0;
3684 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3685 for completion in completions {
3686 this.copilot_state.push_completion(completion);
3687 }
3688 this.update_visible_copilot_suggestion(cx);
3689 }
3690 })
3691 .log_err()?;
3692 Some(())
3693 });
3694
3695 Some(())
3696 }
3697
3698 fn cycle_copilot_suggestions(
3699 &mut self,
3700 direction: Direction,
3701 cx: &mut ViewContext<Self>,
3702 ) -> Option<()> {
3703 let copilot = Copilot::global(cx)?;
3704 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3705 return None;
3706 }
3707
3708 if self.copilot_state.cycled {
3709 self.copilot_state.cycle_completions(direction);
3710 self.update_visible_copilot_suggestion(cx);
3711 } else {
3712 let cursor = self.selections.newest_anchor().head();
3713 let (buffer, buffer_position) =
3714 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3715 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3716 let completions = copilot
3717 .update(&mut cx, |copilot, cx| {
3718 copilot.completions_cycling(&buffer, buffer_position, cx)
3719 })
3720 .await;
3721
3722 this.update(&mut cx, |this, cx| {
3723 this.copilot_state.cycled = true;
3724 for completion in completions.log_err().into_iter().flatten() {
3725 this.copilot_state.push_completion(completion);
3726 }
3727 this.copilot_state.cycle_completions(direction);
3728 this.update_visible_copilot_suggestion(cx);
3729 })
3730 .log_err()?;
3731
3732 Some(())
3733 });
3734 }
3735
3736 Some(())
3737 }
3738
3739 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3740 if !self.has_active_copilot_suggestion(cx) {
3741 self.refresh_copilot_suggestions(false, cx);
3742 return;
3743 }
3744
3745 self.update_visible_copilot_suggestion(cx);
3746 }
3747
3748 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3749 if self.has_active_copilot_suggestion(cx) {
3750 self.cycle_copilot_suggestions(Direction::Next, cx);
3751 } else {
3752 self.refresh_copilot_suggestions(false, cx);
3753 }
3754 }
3755
3756 fn previous_copilot_suggestion(
3757 &mut self,
3758 _: &copilot::PreviousSuggestion,
3759 cx: &mut ViewContext<Self>,
3760 ) {
3761 if self.has_active_copilot_suggestion(cx) {
3762 self.cycle_copilot_suggestions(Direction::Prev, cx);
3763 } else {
3764 self.refresh_copilot_suggestions(false, cx);
3765 }
3766 }
3767
3768 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3769 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3770 if let Some((copilot, completion)) =
3771 Copilot::global(cx).zip(self.copilot_state.active_completion())
3772 {
3773 copilot
3774 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3775 .detach_and_log_err(cx);
3776
3777 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3778 }
3779 cx.emit(Event::InputHandled {
3780 utf16_range_to_replace: None,
3781 text: suggestion.text.to_string().into(),
3782 });
3783 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3784 cx.notify();
3785 true
3786 } else {
3787 false
3788 }
3789 }
3790
3791 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3792 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3793 if let Some(copilot) = Copilot::global(cx) {
3794 copilot
3795 .update(cx, |copilot, cx| {
3796 copilot.discard_completions(&self.copilot_state.completions, cx)
3797 })
3798 .detach_and_log_err(cx);
3799
3800 self.report_copilot_event(None, false, cx)
3801 }
3802
3803 self.display_map.update(cx, |map, cx| {
3804 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3805 });
3806 cx.notify();
3807 true
3808 } else {
3809 false
3810 }
3811 }
3812
3813 fn is_copilot_enabled_at(
3814 &self,
3815 location: Anchor,
3816 snapshot: &MultiBufferSnapshot,
3817 cx: &mut ViewContext<Self>,
3818 ) -> bool {
3819 let file = snapshot.file_at(location);
3820 let language = snapshot.language_at(location);
3821 let settings = all_language_settings(file, cx);
3822 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3823 }
3824
3825 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3826 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3827 let buffer = self.buffer.read(cx).read(cx);
3828 suggestion.position.is_valid(&buffer)
3829 } else {
3830 false
3831 }
3832 }
3833
3834 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3835 let suggestion = self.copilot_state.suggestion.take()?;
3836 self.display_map.update(cx, |map, cx| {
3837 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3838 });
3839 let buffer = self.buffer.read(cx).read(cx);
3840
3841 if suggestion.position.is_valid(&buffer) {
3842 Some(suggestion)
3843 } else {
3844 None
3845 }
3846 }
3847
3848 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3849 let snapshot = self.buffer.read(cx).snapshot(cx);
3850 let selection = self.selections.newest_anchor();
3851 let cursor = selection.head();
3852
3853 if self.context_menu.is_some()
3854 || !self.completion_tasks.is_empty()
3855 || selection.start != selection.end
3856 {
3857 self.discard_copilot_suggestion(cx);
3858 } else if let Some(text) = self
3859 .copilot_state
3860 .text_for_active_completion(cursor, &snapshot)
3861 {
3862 let text = Rope::from(text);
3863 let mut to_remove = Vec::new();
3864 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3865 to_remove.push(suggestion.id);
3866 }
3867
3868 let suggestion_inlay =
3869 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3870 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3871 self.display_map.update(cx, move |map, cx| {
3872 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3873 });
3874 cx.notify();
3875 } else {
3876 self.discard_copilot_suggestion(cx);
3877 }
3878 }
3879
3880 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3881 self.copilot_state = Default::default();
3882 self.discard_copilot_suggestion(cx);
3883 }
3884
3885 pub fn render_code_actions_indicator(
3886 &self,
3887 style: &EditorStyle,
3888 is_active: bool,
3889 cx: &mut ViewContext<Self>,
3890 ) -> Option<AnyElement<Self>> {
3891 if self.available_code_actions.is_some() {
3892 enum CodeActions {}
3893 Some(
3894 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
3895 Svg::new("icons/bolt.svg").with_color(
3896 style
3897 .code_actions
3898 .indicator
3899 .in_state(is_active)
3900 .style_for(state)
3901 .color,
3902 )
3903 })
3904 .with_cursor_style(CursorStyle::PointingHand)
3905 .with_padding(Padding::uniform(3.))
3906 .on_down(MouseButton::Left, |_, this, cx| {
3907 this.toggle_code_actions(
3908 &ToggleCodeActions {
3909 deployed_from_indicator: true,
3910 },
3911 cx,
3912 );
3913 })
3914 .into_any(),
3915 )
3916 } else {
3917 None
3918 }
3919 }
3920
3921 pub fn render_fold_indicators(
3922 &self,
3923 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3924 style: &EditorStyle,
3925 gutter_hovered: bool,
3926 line_height: f32,
3927 gutter_margin: f32,
3928 cx: &mut ViewContext<Self>,
3929 ) -> Vec<Option<AnyElement<Self>>> {
3930 enum FoldIndicators {}
3931
3932 let style = style.folds.clone();
3933
3934 fold_data
3935 .iter()
3936 .enumerate()
3937 .map(|(ix, fold_data)| {
3938 fold_data
3939 .map(|(fold_status, buffer_row, active)| {
3940 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3941 MouseEventHandler::new::<FoldIndicators, _>(
3942 ix as usize,
3943 cx,
3944 |mouse_state, _| {
3945 Svg::new(match fold_status {
3946 FoldStatus::Folded => style.folded_icon.clone(),
3947 FoldStatus::Foldable => style.foldable_icon.clone(),
3948 })
3949 .with_color(
3950 style
3951 .indicator
3952 .in_state(fold_status == FoldStatus::Folded)
3953 .style_for(mouse_state)
3954 .color,
3955 )
3956 .constrained()
3957 .with_width(gutter_margin * style.icon_margin_scale)
3958 .aligned()
3959 .constrained()
3960 .with_height(line_height)
3961 .with_width(gutter_margin)
3962 .aligned()
3963 },
3964 )
3965 .with_cursor_style(CursorStyle::PointingHand)
3966 .with_padding(Padding::uniform(3.))
3967 .on_click(MouseButton::Left, {
3968 move |_, editor, cx| match fold_status {
3969 FoldStatus::Folded => {
3970 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3971 }
3972 FoldStatus::Foldable => {
3973 editor.fold_at(&FoldAt { buffer_row }, cx);
3974 }
3975 }
3976 })
3977 .into_any()
3978 })
3979 })
3980 .flatten()
3981 })
3982 .collect()
3983 }
3984
3985 pub fn context_menu_visible(&self) -> bool {
3986 self.context_menu
3987 .as_ref()
3988 .map_or(false, |menu| menu.visible())
3989 }
3990
3991 pub fn render_context_menu(
3992 &self,
3993 cursor_position: DisplayPoint,
3994 style: EditorStyle,
3995 cx: &mut ViewContext<Editor>,
3996 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3997 self.context_menu
3998 .as_ref()
3999 .map(|menu| menu.render(cursor_position, style, cx))
4000 }
4001
4002 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
4003 if !matches!(menu, ContextMenu::Completions(_)) {
4004 self.completion_tasks.clear();
4005 }
4006 self.context_menu = Some(menu);
4007 self.discard_copilot_suggestion(cx);
4008 cx.notify();
4009 }
4010
4011 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
4012 cx.notify();
4013 self.completion_tasks.clear();
4014 let context_menu = self.context_menu.take();
4015 if context_menu.is_some() {
4016 self.update_visible_copilot_suggestion(cx);
4017 }
4018 context_menu
4019 }
4020
4021 pub fn insert_snippet(
4022 &mut self,
4023 insertion_ranges: &[Range<usize>],
4024 snippet: Snippet,
4025 cx: &mut ViewContext<Self>,
4026 ) -> Result<()> {
4027 let tabstops = self.buffer.update(cx, |buffer, cx| {
4028 let snippet_text: Arc<str> = snippet.text.clone().into();
4029 buffer.edit(
4030 insertion_ranges
4031 .iter()
4032 .cloned()
4033 .map(|range| (range, snippet_text.clone())),
4034 Some(AutoindentMode::EachLine),
4035 cx,
4036 );
4037
4038 let snapshot = &*buffer.read(cx);
4039 let snippet = &snippet;
4040 snippet
4041 .tabstops
4042 .iter()
4043 .map(|tabstop| {
4044 let mut tabstop_ranges = tabstop
4045 .iter()
4046 .flat_map(|tabstop_range| {
4047 let mut delta = 0_isize;
4048 insertion_ranges.iter().map(move |insertion_range| {
4049 let insertion_start = insertion_range.start as isize + delta;
4050 delta +=
4051 snippet.text.len() as isize - insertion_range.len() as isize;
4052
4053 let start = snapshot.anchor_before(
4054 (insertion_start + tabstop_range.start) as usize,
4055 );
4056 let end = snapshot
4057 .anchor_after((insertion_start + tabstop_range.end) as usize);
4058 start..end
4059 })
4060 })
4061 .collect::<Vec<_>>();
4062 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
4063 tabstop_ranges
4064 })
4065 .collect::<Vec<_>>()
4066 });
4067
4068 if let Some(tabstop) = tabstops.first() {
4069 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4070 s.select_ranges(tabstop.iter().cloned());
4071 });
4072 self.snippet_stack.push(SnippetState {
4073 active_index: 0,
4074 ranges: tabstops,
4075 });
4076 }
4077
4078 Ok(())
4079 }
4080
4081 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4082 self.move_to_snippet_tabstop(Bias::Right, cx)
4083 }
4084
4085 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
4086 self.move_to_snippet_tabstop(Bias::Left, cx)
4087 }
4088
4089 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
4090 if let Some(mut snippet) = self.snippet_stack.pop() {
4091 match bias {
4092 Bias::Left => {
4093 if snippet.active_index > 0 {
4094 snippet.active_index -= 1;
4095 } else {
4096 self.snippet_stack.push(snippet);
4097 return false;
4098 }
4099 }
4100 Bias::Right => {
4101 if snippet.active_index + 1 < snippet.ranges.len() {
4102 snippet.active_index += 1;
4103 } else {
4104 self.snippet_stack.push(snippet);
4105 return false;
4106 }
4107 }
4108 }
4109 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
4110 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4111 s.select_anchor_ranges(current_ranges.iter().cloned())
4112 });
4113 // If snippet state is not at the last tabstop, push it back on the stack
4114 if snippet.active_index + 1 < snippet.ranges.len() {
4115 self.snippet_stack.push(snippet);
4116 }
4117 return true;
4118 }
4119 }
4120
4121 false
4122 }
4123
4124 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
4125 self.transact(cx, |this, cx| {
4126 this.select_all(&SelectAll, cx);
4127 this.insert("", cx);
4128 });
4129 }
4130
4131 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
4132 self.transact(cx, |this, cx| {
4133 this.select_autoclose_pair(cx);
4134 let mut selections = this.selections.all::<Point>(cx);
4135 if !this.selections.line_mode {
4136 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4137 for selection in &mut selections {
4138 if selection.is_empty() {
4139 let old_head = selection.head();
4140 let mut new_head =
4141 movement::left(&display_map, old_head.to_display_point(&display_map))
4142 .to_point(&display_map);
4143 if let Some((buffer, line_buffer_range)) = display_map
4144 .buffer_snapshot
4145 .buffer_line_for_row(old_head.row)
4146 {
4147 let indent_size =
4148 buffer.indent_size_for_line(line_buffer_range.start.row);
4149 let indent_len = match indent_size.kind {
4150 IndentKind::Space => {
4151 buffer.settings_at(line_buffer_range.start, cx).tab_size
4152 }
4153 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
4154 };
4155 if old_head.column <= indent_size.len && old_head.column > 0 {
4156 let indent_len = indent_len.get();
4157 new_head = cmp::min(
4158 new_head,
4159 Point::new(
4160 old_head.row,
4161 ((old_head.column - 1) / indent_len) * indent_len,
4162 ),
4163 );
4164 }
4165 }
4166
4167 selection.set_head(new_head, SelectionGoal::None);
4168 }
4169 }
4170 }
4171
4172 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4173 this.insert("", cx);
4174 this.refresh_copilot_suggestions(true, cx);
4175 });
4176 }
4177
4178 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
4179 self.transact(cx, |this, cx| {
4180 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4181 let line_mode = s.line_mode;
4182 s.move_with(|map, selection| {
4183 if selection.is_empty() && !line_mode {
4184 let cursor = movement::right(map, selection.head());
4185 selection.end = cursor;
4186 selection.reversed = true;
4187 selection.goal = SelectionGoal::None;
4188 }
4189 })
4190 });
4191 this.insert("", cx);
4192 this.refresh_copilot_suggestions(true, cx);
4193 });
4194 }
4195
4196 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
4197 if self.move_to_prev_snippet_tabstop(cx) {
4198 return;
4199 }
4200
4201 self.outdent(&Outdent, cx);
4202 }
4203
4204 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
4205 if self.move_to_next_snippet_tabstop(cx) {
4206 return;
4207 }
4208
4209 let mut selections = self.selections.all_adjusted(cx);
4210 let buffer = self.buffer.read(cx);
4211 let snapshot = buffer.snapshot(cx);
4212 let rows_iter = selections.iter().map(|s| s.head().row);
4213 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
4214
4215 let mut edits = Vec::new();
4216 let mut prev_edited_row = 0;
4217 let mut row_delta = 0;
4218 for selection in &mut selections {
4219 if selection.start.row != prev_edited_row {
4220 row_delta = 0;
4221 }
4222 prev_edited_row = selection.end.row;
4223
4224 // If the selection is non-empty, then increase the indentation of the selected lines.
4225 if !selection.is_empty() {
4226 row_delta =
4227 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4228 continue;
4229 }
4230
4231 // If the selection is empty and the cursor is in the leading whitespace before the
4232 // suggested indentation, then auto-indent the line.
4233 let cursor = selection.head();
4234 let current_indent = snapshot.indent_size_for_line(cursor.row);
4235 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
4236 if cursor.column < suggested_indent.len
4237 && cursor.column <= current_indent.len
4238 && current_indent.len <= suggested_indent.len
4239 {
4240 selection.start = Point::new(cursor.row, suggested_indent.len);
4241 selection.end = selection.start;
4242 if row_delta == 0 {
4243 edits.extend(Buffer::edit_for_indent_size_adjustment(
4244 cursor.row,
4245 current_indent,
4246 suggested_indent,
4247 ));
4248 row_delta = suggested_indent.len - current_indent.len;
4249 }
4250 continue;
4251 }
4252 }
4253
4254 // Accept copilot suggestion if there is only one selection and the cursor is not
4255 // in the leading whitespace.
4256 if self.selections.count() == 1
4257 && cursor.column >= current_indent.len
4258 && self.has_active_copilot_suggestion(cx)
4259 {
4260 self.accept_copilot_suggestion(cx);
4261 return;
4262 }
4263
4264 // Otherwise, insert a hard or soft tab.
4265 let settings = buffer.settings_at(cursor, cx);
4266 let tab_size = if settings.hard_tabs {
4267 IndentSize::tab()
4268 } else {
4269 let tab_size = settings.tab_size.get();
4270 let char_column = snapshot
4271 .text_for_range(Point::new(cursor.row, 0)..cursor)
4272 .flat_map(str::chars)
4273 .count()
4274 + row_delta as usize;
4275 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
4276 IndentSize::spaces(chars_to_next_tab_stop)
4277 };
4278 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
4279 selection.end = selection.start;
4280 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
4281 row_delta += tab_size.len;
4282 }
4283
4284 self.transact(cx, |this, cx| {
4285 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4286 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4287 this.refresh_copilot_suggestions(true, cx);
4288 });
4289 }
4290
4291 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
4292 let mut selections = self.selections.all::<Point>(cx);
4293 let mut prev_edited_row = 0;
4294 let mut row_delta = 0;
4295 let mut edits = Vec::new();
4296 let buffer = self.buffer.read(cx);
4297 let snapshot = buffer.snapshot(cx);
4298 for selection in &mut selections {
4299 if selection.start.row != prev_edited_row {
4300 row_delta = 0;
4301 }
4302 prev_edited_row = selection.end.row;
4303
4304 row_delta =
4305 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
4306 }
4307
4308 self.transact(cx, |this, cx| {
4309 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
4310 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4311 });
4312 }
4313
4314 fn indent_selection(
4315 buffer: &MultiBuffer,
4316 snapshot: &MultiBufferSnapshot,
4317 selection: &mut Selection<Point>,
4318 edits: &mut Vec<(Range<Point>, String)>,
4319 delta_for_start_row: u32,
4320 cx: &AppContext,
4321 ) -> u32 {
4322 let settings = buffer.settings_at(selection.start, cx);
4323 let tab_size = settings.tab_size.get();
4324 let indent_kind = if settings.hard_tabs {
4325 IndentKind::Tab
4326 } else {
4327 IndentKind::Space
4328 };
4329 let mut start_row = selection.start.row;
4330 let mut end_row = selection.end.row + 1;
4331
4332 // If a selection ends at the beginning of a line, don't indent
4333 // that last line.
4334 if selection.end.column == 0 {
4335 end_row -= 1;
4336 }
4337
4338 // Avoid re-indenting a row that has already been indented by a
4339 // previous selection, but still update this selection's column
4340 // to reflect that indentation.
4341 if delta_for_start_row > 0 {
4342 start_row += 1;
4343 selection.start.column += delta_for_start_row;
4344 if selection.end.row == selection.start.row {
4345 selection.end.column += delta_for_start_row;
4346 }
4347 }
4348
4349 let mut delta_for_end_row = 0;
4350 for row in start_row..end_row {
4351 let current_indent = snapshot.indent_size_for_line(row);
4352 let indent_delta = match (current_indent.kind, indent_kind) {
4353 (IndentKind::Space, IndentKind::Space) => {
4354 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4355 IndentSize::spaces(columns_to_next_tab_stop)
4356 }
4357 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4358 (_, IndentKind::Tab) => IndentSize::tab(),
4359 };
4360
4361 let row_start = Point::new(row, 0);
4362 edits.push((
4363 row_start..row_start,
4364 indent_delta.chars().collect::<String>(),
4365 ));
4366
4367 // Update this selection's endpoints to reflect the indentation.
4368 if row == selection.start.row {
4369 selection.start.column += indent_delta.len;
4370 }
4371 if row == selection.end.row {
4372 selection.end.column += indent_delta.len;
4373 delta_for_end_row = indent_delta.len;
4374 }
4375 }
4376
4377 if selection.start.row == selection.end.row {
4378 delta_for_start_row + delta_for_end_row
4379 } else {
4380 delta_for_end_row
4381 }
4382 }
4383
4384 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4385 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4386 let selections = self.selections.all::<Point>(cx);
4387 let mut deletion_ranges = Vec::new();
4388 let mut last_outdent = None;
4389 {
4390 let buffer = self.buffer.read(cx);
4391 let snapshot = buffer.snapshot(cx);
4392 for selection in &selections {
4393 let settings = buffer.settings_at(selection.start, cx);
4394 let tab_size = settings.tab_size.get();
4395 let mut rows = selection.spanned_rows(false, &display_map);
4396
4397 // Avoid re-outdenting a row that has already been outdented by a
4398 // previous selection.
4399 if let Some(last_row) = last_outdent {
4400 if last_row == rows.start {
4401 rows.start += 1;
4402 }
4403 }
4404
4405 for row in rows {
4406 let indent_size = snapshot.indent_size_for_line(row);
4407 if indent_size.len > 0 {
4408 let deletion_len = match indent_size.kind {
4409 IndentKind::Space => {
4410 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4411 if columns_to_prev_tab_stop == 0 {
4412 tab_size
4413 } else {
4414 columns_to_prev_tab_stop
4415 }
4416 }
4417 IndentKind::Tab => 1,
4418 };
4419 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4420 last_outdent = Some(row);
4421 }
4422 }
4423 }
4424 }
4425
4426 self.transact(cx, |this, cx| {
4427 this.buffer.update(cx, |buffer, cx| {
4428 let empty_str: Arc<str> = "".into();
4429 buffer.edit(
4430 deletion_ranges
4431 .into_iter()
4432 .map(|range| (range, empty_str.clone())),
4433 None,
4434 cx,
4435 );
4436 });
4437 let selections = this.selections.all::<usize>(cx);
4438 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4439 });
4440 }
4441
4442 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4443 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4444 let selections = self.selections.all::<Point>(cx);
4445
4446 let mut new_cursors = Vec::new();
4447 let mut edit_ranges = Vec::new();
4448 let mut selections = selections.iter().peekable();
4449 while let Some(selection) = selections.next() {
4450 let mut rows = selection.spanned_rows(false, &display_map);
4451 let goal_display_column = selection.head().to_display_point(&display_map).column();
4452
4453 // Accumulate contiguous regions of rows that we want to delete.
4454 while let Some(next_selection) = selections.peek() {
4455 let next_rows = next_selection.spanned_rows(false, &display_map);
4456 if next_rows.start <= rows.end {
4457 rows.end = next_rows.end;
4458 selections.next().unwrap();
4459 } else {
4460 break;
4461 }
4462 }
4463
4464 let buffer = &display_map.buffer_snapshot;
4465 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4466 let edit_end;
4467 let cursor_buffer_row;
4468 if buffer.max_point().row >= rows.end {
4469 // If there's a line after the range, delete the \n from the end of the row range
4470 // and position the cursor on the next line.
4471 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4472 cursor_buffer_row = rows.end;
4473 } else {
4474 // If there isn't a line after the range, delete the \n from the line before the
4475 // start of the row range and position the cursor there.
4476 edit_start = edit_start.saturating_sub(1);
4477 edit_end = buffer.len();
4478 cursor_buffer_row = rows.start.saturating_sub(1);
4479 }
4480
4481 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4482 *cursor.column_mut() =
4483 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4484
4485 new_cursors.push((
4486 selection.id,
4487 buffer.anchor_after(cursor.to_point(&display_map)),
4488 ));
4489 edit_ranges.push(edit_start..edit_end);
4490 }
4491
4492 self.transact(cx, |this, cx| {
4493 let buffer = this.buffer.update(cx, |buffer, cx| {
4494 let empty_str: Arc<str> = "".into();
4495 buffer.edit(
4496 edit_ranges
4497 .into_iter()
4498 .map(|range| (range, empty_str.clone())),
4499 None,
4500 cx,
4501 );
4502 buffer.snapshot(cx)
4503 });
4504 let new_selections = new_cursors
4505 .into_iter()
4506 .map(|(id, cursor)| {
4507 let cursor = cursor.to_point(&buffer);
4508 Selection {
4509 id,
4510 start: cursor,
4511 end: cursor,
4512 reversed: false,
4513 goal: SelectionGoal::None,
4514 }
4515 })
4516 .collect();
4517
4518 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4519 s.select(new_selections);
4520 });
4521 });
4522 }
4523
4524 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4525 let mut row_ranges = Vec::<Range<u32>>::new();
4526 for selection in self.selections.all::<Point>(cx) {
4527 let start = selection.start.row;
4528 let end = if selection.start.row == selection.end.row {
4529 selection.start.row + 1
4530 } else {
4531 selection.end.row
4532 };
4533
4534 if let Some(last_row_range) = row_ranges.last_mut() {
4535 if start <= last_row_range.end {
4536 last_row_range.end = end;
4537 continue;
4538 }
4539 }
4540 row_ranges.push(start..end);
4541 }
4542
4543 let snapshot = self.buffer.read(cx).snapshot(cx);
4544 let mut cursor_positions = Vec::new();
4545 for row_range in &row_ranges {
4546 let anchor = snapshot.anchor_before(Point::new(
4547 row_range.end - 1,
4548 snapshot.line_len(row_range.end - 1),
4549 ));
4550 cursor_positions.push(anchor.clone()..anchor);
4551 }
4552
4553 self.transact(cx, |this, cx| {
4554 for row_range in row_ranges.into_iter().rev() {
4555 for row in row_range.rev() {
4556 let end_of_line = Point::new(row, snapshot.line_len(row));
4557 let indent = snapshot.indent_size_for_line(row + 1);
4558 let start_of_next_line = Point::new(row + 1, indent.len);
4559
4560 let replace = if snapshot.line_len(row + 1) > indent.len {
4561 " "
4562 } else {
4563 ""
4564 };
4565
4566 this.buffer.update(cx, |buffer, cx| {
4567 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4568 });
4569 }
4570 }
4571
4572 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4573 s.select_anchor_ranges(cursor_positions)
4574 });
4575 });
4576 }
4577
4578 pub fn sort_lines_case_sensitive(
4579 &mut self,
4580 _: &SortLinesCaseSensitive,
4581 cx: &mut ViewContext<Self>,
4582 ) {
4583 self.manipulate_lines(cx, |lines| lines.sort())
4584 }
4585
4586 pub fn sort_lines_case_insensitive(
4587 &mut self,
4588 _: &SortLinesCaseInsensitive,
4589 cx: &mut ViewContext<Self>,
4590 ) {
4591 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4592 }
4593
4594 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4595 self.manipulate_lines(cx, |lines| lines.reverse())
4596 }
4597
4598 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4599 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4600 }
4601
4602 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4603 where
4604 Fn: FnMut(&mut [&str]),
4605 {
4606 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4607 let buffer = self.buffer.read(cx).snapshot(cx);
4608
4609 let mut edits = Vec::new();
4610
4611 let selections = self.selections.all::<Point>(cx);
4612 let mut selections = selections.iter().peekable();
4613 let mut contiguous_row_selections = Vec::new();
4614 let mut new_selections = Vec::new();
4615
4616 while let Some(selection) = selections.next() {
4617 let (start_row, end_row) = consume_contiguous_rows(
4618 &mut contiguous_row_selections,
4619 selection,
4620 &display_map,
4621 &mut selections,
4622 );
4623
4624 let start_point = Point::new(start_row, 0);
4625 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4626 let text = buffer
4627 .text_for_range(start_point..end_point)
4628 .collect::<String>();
4629 let mut lines = text.split("\n").collect_vec();
4630
4631 let lines_len = lines.len();
4632 callback(&mut lines);
4633
4634 // This is a current limitation with selections.
4635 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4636 debug_assert!(
4637 lines.len() == lines_len,
4638 "callback should not change the number of lines"
4639 );
4640
4641 edits.push((start_point..end_point, lines.join("\n")));
4642 let start_anchor = buffer.anchor_after(start_point);
4643 let end_anchor = buffer.anchor_before(end_point);
4644
4645 // Make selection and push
4646 new_selections.push(Selection {
4647 id: selection.id,
4648 start: start_anchor.to_offset(&buffer),
4649 end: end_anchor.to_offset(&buffer),
4650 goal: SelectionGoal::None,
4651 reversed: selection.reversed,
4652 });
4653 }
4654
4655 self.transact(cx, |this, cx| {
4656 this.buffer.update(cx, |buffer, cx| {
4657 buffer.edit(edits, None, cx);
4658 });
4659
4660 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4661 s.select(new_selections);
4662 });
4663
4664 this.request_autoscroll(Autoscroll::fit(), cx);
4665 });
4666 }
4667
4668 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4669 self.manipulate_text(cx, |text| text.to_uppercase())
4670 }
4671
4672 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4673 self.manipulate_text(cx, |text| text.to_lowercase())
4674 }
4675
4676 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4677 self.manipulate_text(cx, |text| {
4678 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4679 // https://github.com/rutrum/convert-case/issues/16
4680 text.split("\n")
4681 .map(|line| line.to_case(Case::Title))
4682 .join("\n")
4683 })
4684 }
4685
4686 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4687 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4688 }
4689
4690 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4691 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4692 }
4693
4694 pub fn convert_to_upper_camel_case(
4695 &mut self,
4696 _: &ConvertToUpperCamelCase,
4697 cx: &mut ViewContext<Self>,
4698 ) {
4699 self.manipulate_text(cx, |text| {
4700 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
4701 // https://github.com/rutrum/convert-case/issues/16
4702 text.split("\n")
4703 .map(|line| line.to_case(Case::UpperCamel))
4704 .join("\n")
4705 })
4706 }
4707
4708 pub fn convert_to_lower_camel_case(
4709 &mut self,
4710 _: &ConvertToLowerCamelCase,
4711 cx: &mut ViewContext<Self>,
4712 ) {
4713 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4714 }
4715
4716 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4717 where
4718 Fn: FnMut(&str) -> String,
4719 {
4720 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4721 let buffer = self.buffer.read(cx).snapshot(cx);
4722
4723 let mut new_selections = Vec::new();
4724 let mut edits = Vec::new();
4725 let mut selection_adjustment = 0i32;
4726
4727 for selection in self.selections.all::<usize>(cx) {
4728 let selection_is_empty = selection.is_empty();
4729
4730 let (start, end) = if selection_is_empty {
4731 let word_range = movement::surrounding_word(
4732 &display_map,
4733 selection.start.to_display_point(&display_map),
4734 );
4735 let start = word_range.start.to_offset(&display_map, Bias::Left);
4736 let end = word_range.end.to_offset(&display_map, Bias::Left);
4737 (start, end)
4738 } else {
4739 (selection.start, selection.end)
4740 };
4741
4742 let text = buffer.text_for_range(start..end).collect::<String>();
4743 let old_length = text.len() as i32;
4744 let text = callback(&text);
4745
4746 new_selections.push(Selection {
4747 start: (start as i32 - selection_adjustment) as usize,
4748 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4749 goal: SelectionGoal::None,
4750 ..selection
4751 });
4752
4753 selection_adjustment += old_length - text.len() as i32;
4754
4755 edits.push((start..end, text));
4756 }
4757
4758 self.transact(cx, |this, cx| {
4759 this.buffer.update(cx, |buffer, cx| {
4760 buffer.edit(edits, None, cx);
4761 });
4762
4763 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4764 s.select(new_selections);
4765 });
4766
4767 this.request_autoscroll(Autoscroll::fit(), cx);
4768 });
4769 }
4770
4771 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4772 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4773 let buffer = &display_map.buffer_snapshot;
4774 let selections = self.selections.all::<Point>(cx);
4775
4776 let mut edits = Vec::new();
4777 let mut selections_iter = selections.iter().peekable();
4778 while let Some(selection) = selections_iter.next() {
4779 // Avoid duplicating the same lines twice.
4780 let mut rows = selection.spanned_rows(false, &display_map);
4781
4782 while let Some(next_selection) = selections_iter.peek() {
4783 let next_rows = next_selection.spanned_rows(false, &display_map);
4784 if next_rows.start < rows.end {
4785 rows.end = next_rows.end;
4786 selections_iter.next().unwrap();
4787 } else {
4788 break;
4789 }
4790 }
4791
4792 // Copy the text from the selected row region and splice it at the start of the region.
4793 let start = Point::new(rows.start, 0);
4794 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4795 let text = buffer
4796 .text_for_range(start..end)
4797 .chain(Some("\n"))
4798 .collect::<String>();
4799 edits.push((start..start, text));
4800 }
4801
4802 self.transact(cx, |this, cx| {
4803 this.buffer.update(cx, |buffer, cx| {
4804 buffer.edit(edits, None, cx);
4805 });
4806
4807 this.request_autoscroll(Autoscroll::fit(), cx);
4808 });
4809 }
4810
4811 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4812 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4813 let buffer = self.buffer.read(cx).snapshot(cx);
4814
4815 let mut edits = Vec::new();
4816 let mut unfold_ranges = Vec::new();
4817 let mut refold_ranges = Vec::new();
4818
4819 let selections = self.selections.all::<Point>(cx);
4820 let mut selections = selections.iter().peekable();
4821 let mut contiguous_row_selections = Vec::new();
4822 let mut new_selections = Vec::new();
4823
4824 while let Some(selection) = selections.next() {
4825 // Find all the selections that span a contiguous row range
4826 let (start_row, end_row) = consume_contiguous_rows(
4827 &mut contiguous_row_selections,
4828 selection,
4829 &display_map,
4830 &mut selections,
4831 );
4832
4833 // Move the text spanned by the row range to be before the line preceding the row range
4834 if start_row > 0 {
4835 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4836 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4837 let insertion_point = display_map
4838 .prev_line_boundary(Point::new(start_row - 1, 0))
4839 .0;
4840
4841 // Don't move lines across excerpts
4842 if buffer
4843 .excerpt_boundaries_in_range((
4844 Bound::Excluded(insertion_point),
4845 Bound::Included(range_to_move.end),
4846 ))
4847 .next()
4848 .is_none()
4849 {
4850 let text = buffer
4851 .text_for_range(range_to_move.clone())
4852 .flat_map(|s| s.chars())
4853 .skip(1)
4854 .chain(['\n'])
4855 .collect::<String>();
4856
4857 edits.push((
4858 buffer.anchor_after(range_to_move.start)
4859 ..buffer.anchor_before(range_to_move.end),
4860 String::new(),
4861 ));
4862 let insertion_anchor = buffer.anchor_after(insertion_point);
4863 edits.push((insertion_anchor..insertion_anchor, text));
4864
4865 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4866
4867 // Move selections up
4868 new_selections.extend(contiguous_row_selections.drain(..).map(
4869 |mut selection| {
4870 selection.start.row -= row_delta;
4871 selection.end.row -= row_delta;
4872 selection
4873 },
4874 ));
4875
4876 // Move folds up
4877 unfold_ranges.push(range_to_move.clone());
4878 for fold in display_map.folds_in_range(
4879 buffer.anchor_before(range_to_move.start)
4880 ..buffer.anchor_after(range_to_move.end),
4881 ) {
4882 let mut start = fold.start.to_point(&buffer);
4883 let mut end = fold.end.to_point(&buffer);
4884 start.row -= row_delta;
4885 end.row -= row_delta;
4886 refold_ranges.push(start..end);
4887 }
4888 }
4889 }
4890
4891 // If we didn't move line(s), preserve the existing selections
4892 new_selections.append(&mut contiguous_row_selections);
4893 }
4894
4895 self.transact(cx, |this, cx| {
4896 this.unfold_ranges(unfold_ranges, true, true, cx);
4897 this.buffer.update(cx, |buffer, cx| {
4898 for (range, text) in edits {
4899 buffer.edit([(range, text)], None, cx);
4900 }
4901 });
4902 this.fold_ranges(refold_ranges, true, cx);
4903 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4904 s.select(new_selections);
4905 })
4906 });
4907 }
4908
4909 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4910 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4911 let buffer = self.buffer.read(cx).snapshot(cx);
4912
4913 let mut edits = Vec::new();
4914 let mut unfold_ranges = Vec::new();
4915 let mut refold_ranges = Vec::new();
4916
4917 let selections = self.selections.all::<Point>(cx);
4918 let mut selections = selections.iter().peekable();
4919 let mut contiguous_row_selections = Vec::new();
4920 let mut new_selections = Vec::new();
4921
4922 while let Some(selection) = selections.next() {
4923 // Find all the selections that span a contiguous row range
4924 let (start_row, end_row) = consume_contiguous_rows(
4925 &mut contiguous_row_selections,
4926 selection,
4927 &display_map,
4928 &mut selections,
4929 );
4930
4931 // Move the text spanned by the row range to be after the last line of the row range
4932 if end_row <= buffer.max_point().row {
4933 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4934 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4935
4936 // Don't move lines across excerpt boundaries
4937 if buffer
4938 .excerpt_boundaries_in_range((
4939 Bound::Excluded(range_to_move.start),
4940 Bound::Included(insertion_point),
4941 ))
4942 .next()
4943 .is_none()
4944 {
4945 let mut text = String::from("\n");
4946 text.extend(buffer.text_for_range(range_to_move.clone()));
4947 text.pop(); // Drop trailing newline
4948 edits.push((
4949 buffer.anchor_after(range_to_move.start)
4950 ..buffer.anchor_before(range_to_move.end),
4951 String::new(),
4952 ));
4953 let insertion_anchor = buffer.anchor_after(insertion_point);
4954 edits.push((insertion_anchor..insertion_anchor, text));
4955
4956 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4957
4958 // Move selections down
4959 new_selections.extend(contiguous_row_selections.drain(..).map(
4960 |mut selection| {
4961 selection.start.row += row_delta;
4962 selection.end.row += row_delta;
4963 selection
4964 },
4965 ));
4966
4967 // Move folds down
4968 unfold_ranges.push(range_to_move.clone());
4969 for fold in display_map.folds_in_range(
4970 buffer.anchor_before(range_to_move.start)
4971 ..buffer.anchor_after(range_to_move.end),
4972 ) {
4973 let mut start = fold.start.to_point(&buffer);
4974 let mut end = fold.end.to_point(&buffer);
4975 start.row += row_delta;
4976 end.row += row_delta;
4977 refold_ranges.push(start..end);
4978 }
4979 }
4980 }
4981
4982 // If we didn't move line(s), preserve the existing selections
4983 new_selections.append(&mut contiguous_row_selections);
4984 }
4985
4986 self.transact(cx, |this, cx| {
4987 this.unfold_ranges(unfold_ranges, true, true, cx);
4988 this.buffer.update(cx, |buffer, cx| {
4989 for (range, text) in edits {
4990 buffer.edit([(range, text)], None, cx);
4991 }
4992 });
4993 this.fold_ranges(refold_ranges, true, cx);
4994 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4995 });
4996 }
4997
4998 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4999 let text_layout_details = &self.text_layout_details(cx);
5000 self.transact(cx, |this, cx| {
5001 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5002 let mut edits: Vec<(Range<usize>, String)> = Default::default();
5003 let line_mode = s.line_mode;
5004 s.move_with(|display_map, selection| {
5005 if !selection.is_empty() || line_mode {
5006 return;
5007 }
5008
5009 let mut head = selection.head();
5010 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
5011 if head.column() == display_map.line_len(head.row()) {
5012 transpose_offset = display_map
5013 .buffer_snapshot
5014 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5015 }
5016
5017 if transpose_offset == 0 {
5018 return;
5019 }
5020
5021 *head.column_mut() += 1;
5022 head = display_map.clip_point(head, Bias::Right);
5023 let goal = SelectionGoal::HorizontalPosition(
5024 display_map.x_for_point(head, &text_layout_details),
5025 );
5026 selection.collapse_to(head, goal);
5027
5028 let transpose_start = display_map
5029 .buffer_snapshot
5030 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
5031 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
5032 let transpose_end = display_map
5033 .buffer_snapshot
5034 .clip_offset(transpose_offset + 1, Bias::Right);
5035 if let Some(ch) =
5036 display_map.buffer_snapshot.chars_at(transpose_start).next()
5037 {
5038 edits.push((transpose_start..transpose_offset, String::new()));
5039 edits.push((transpose_end..transpose_end, ch.to_string()));
5040 }
5041 }
5042 });
5043 edits
5044 });
5045 this.buffer
5046 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5047 let selections = this.selections.all::<usize>(cx);
5048 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5049 s.select(selections);
5050 });
5051 });
5052 }
5053
5054 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5055 let mut text = String::new();
5056 let buffer = self.buffer.read(cx).snapshot(cx);
5057 let mut selections = self.selections.all::<Point>(cx);
5058 let mut clipboard_selections = Vec::with_capacity(selections.len());
5059 {
5060 let max_point = buffer.max_point();
5061 let mut is_first = true;
5062 for selection in &mut selections {
5063 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5064 if is_entire_line {
5065 selection.start = Point::new(selection.start.row, 0);
5066 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5067 selection.goal = SelectionGoal::None;
5068 }
5069 if is_first {
5070 is_first = false;
5071 } else {
5072 text += "\n";
5073 }
5074 let mut len = 0;
5075 for chunk in buffer.text_for_range(selection.start..selection.end) {
5076 text.push_str(chunk);
5077 len += chunk.len();
5078 }
5079 clipboard_selections.push(ClipboardSelection {
5080 len,
5081 is_entire_line,
5082 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5083 });
5084 }
5085 }
5086
5087 self.transact(cx, |this, cx| {
5088 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5089 s.select(selections);
5090 });
5091 this.insert("", cx);
5092 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5093 });
5094 }
5095
5096 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5097 let selections = self.selections.all::<Point>(cx);
5098 let buffer = self.buffer.read(cx).read(cx);
5099 let mut text = String::new();
5100
5101 let mut clipboard_selections = Vec::with_capacity(selections.len());
5102 {
5103 let max_point = buffer.max_point();
5104 let mut is_first = true;
5105 for selection in selections.iter() {
5106 let mut start = selection.start;
5107 let mut end = selection.end;
5108 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5109 if is_entire_line {
5110 start = Point::new(start.row, 0);
5111 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5112 }
5113 if is_first {
5114 is_first = false;
5115 } else {
5116 text += "\n";
5117 }
5118 let mut len = 0;
5119 for chunk in buffer.text_for_range(start..end) {
5120 text.push_str(chunk);
5121 len += chunk.len();
5122 }
5123 clipboard_selections.push(ClipboardSelection {
5124 len,
5125 is_entire_line,
5126 first_line_indent: buffer.indent_size_for_line(start.row).len,
5127 });
5128 }
5129 }
5130
5131 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5132 }
5133
5134 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5135 self.transact(cx, |this, cx| {
5136 if let Some(item) = cx.read_from_clipboard() {
5137 let clipboard_text = Cow::Borrowed(item.text());
5138 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5139 let old_selections = this.selections.all::<usize>(cx);
5140 let all_selections_were_entire_line =
5141 clipboard_selections.iter().all(|s| s.is_entire_line);
5142 let first_selection_indent_column =
5143 clipboard_selections.first().map(|s| s.first_line_indent);
5144 if clipboard_selections.len() != old_selections.len() {
5145 clipboard_selections.drain(..);
5146 }
5147
5148 this.buffer.update(cx, |buffer, cx| {
5149 let snapshot = buffer.read(cx);
5150 let mut start_offset = 0;
5151 let mut edits = Vec::new();
5152 let mut original_indent_columns = Vec::new();
5153 let line_mode = this.selections.line_mode;
5154 for (ix, selection) in old_selections.iter().enumerate() {
5155 let to_insert;
5156 let entire_line;
5157 let original_indent_column;
5158 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5159 let end_offset = start_offset + clipboard_selection.len;
5160 to_insert = &clipboard_text[start_offset..end_offset];
5161 entire_line = clipboard_selection.is_entire_line;
5162 start_offset = end_offset + 1;
5163 original_indent_column =
5164 Some(clipboard_selection.first_line_indent);
5165 } else {
5166 to_insert = clipboard_text.as_str();
5167 entire_line = all_selections_were_entire_line;
5168 original_indent_column = first_selection_indent_column
5169 }
5170
5171 // If the corresponding selection was empty when this slice of the
5172 // clipboard text was written, then the entire line containing the
5173 // selection was copied. If this selection is also currently empty,
5174 // then paste the line before the current line of the buffer.
5175 let range = if selection.is_empty() && !line_mode && entire_line {
5176 let column = selection.start.to_point(&snapshot).column as usize;
5177 let line_start = selection.start - column;
5178 line_start..line_start
5179 } else {
5180 selection.range()
5181 };
5182
5183 edits.push((range, to_insert));
5184 original_indent_columns.extend(original_indent_column);
5185 }
5186 drop(snapshot);
5187
5188 buffer.edit(
5189 edits,
5190 Some(AutoindentMode::Block {
5191 original_indent_columns,
5192 }),
5193 cx,
5194 );
5195 });
5196
5197 let selections = this.selections.all::<usize>(cx);
5198 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5199 } else {
5200 this.insert(&clipboard_text, cx);
5201 }
5202 }
5203 });
5204 }
5205
5206 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5207 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5208 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5209 self.change_selections(None, cx, |s| {
5210 s.select_anchors(selections.to_vec());
5211 });
5212 }
5213 self.request_autoscroll(Autoscroll::fit(), cx);
5214 self.unmark_text(cx);
5215 self.refresh_copilot_suggestions(true, cx);
5216 cx.emit(Event::Edited);
5217 }
5218 }
5219
5220 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5221 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5222 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5223 {
5224 self.change_selections(None, cx, |s| {
5225 s.select_anchors(selections.to_vec());
5226 });
5227 }
5228 self.request_autoscroll(Autoscroll::fit(), cx);
5229 self.unmark_text(cx);
5230 self.refresh_copilot_suggestions(true, cx);
5231 cx.emit(Event::Edited);
5232 }
5233 }
5234
5235 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5236 self.buffer
5237 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5238 }
5239
5240 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5241 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5242 let line_mode = s.line_mode;
5243 s.move_with(|map, selection| {
5244 let cursor = if selection.is_empty() && !line_mode {
5245 movement::left(map, selection.start)
5246 } else {
5247 selection.start
5248 };
5249 selection.collapse_to(cursor, SelectionGoal::None);
5250 });
5251 })
5252 }
5253
5254 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5255 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5256 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5257 })
5258 }
5259
5260 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5261 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5262 let line_mode = s.line_mode;
5263 s.move_with(|map, selection| {
5264 let cursor = if selection.is_empty() && !line_mode {
5265 movement::right(map, selection.end)
5266 } else {
5267 selection.end
5268 };
5269 selection.collapse_to(cursor, SelectionGoal::None)
5270 });
5271 })
5272 }
5273
5274 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5275 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5276 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5277 })
5278 }
5279
5280 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5281 if self.take_rename(true, cx).is_some() {
5282 return;
5283 }
5284
5285 if matches!(self.mode, EditorMode::SingleLine) {
5286 cx.propagate_action();
5287 return;
5288 }
5289
5290 let text_layout_details = &self.text_layout_details(cx);
5291
5292 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5293 let line_mode = s.line_mode;
5294 s.move_with(|map, selection| {
5295 if !selection.is_empty() && !line_mode {
5296 selection.goal = SelectionGoal::None;
5297 }
5298 let (cursor, goal) = movement::up(
5299 map,
5300 selection.start,
5301 selection.goal,
5302 false,
5303 &text_layout_details,
5304 );
5305 selection.collapse_to(cursor, goal);
5306 });
5307 })
5308 }
5309
5310 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5311 if self.take_rename(true, cx).is_some() {
5312 return;
5313 }
5314
5315 if matches!(self.mode, EditorMode::SingleLine) {
5316 cx.propagate_action();
5317 return;
5318 }
5319
5320 let row_count = if let Some(row_count) = self.visible_line_count() {
5321 row_count as u32 - 1
5322 } else {
5323 return;
5324 };
5325
5326 let autoscroll = if action.center_cursor {
5327 Autoscroll::center()
5328 } else {
5329 Autoscroll::fit()
5330 };
5331
5332 let text_layout_details = &self.text_layout_details(cx);
5333
5334 self.change_selections(Some(autoscroll), cx, |s| {
5335 let line_mode = s.line_mode;
5336 s.move_with(|map, selection| {
5337 if !selection.is_empty() && !line_mode {
5338 selection.goal = SelectionGoal::None;
5339 }
5340 let (cursor, goal) = movement::up_by_rows(
5341 map,
5342 selection.end,
5343 row_count,
5344 selection.goal,
5345 false,
5346 &text_layout_details,
5347 );
5348 selection.collapse_to(cursor, goal);
5349 });
5350 });
5351 }
5352
5353 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5354 let text_layout_details = &self.text_layout_details(cx);
5355 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5356 s.move_heads_with(|map, head, goal| {
5357 movement::up(map, head, goal, false, &text_layout_details)
5358 })
5359 })
5360 }
5361
5362 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5363 self.take_rename(true, cx);
5364
5365 if self.mode == EditorMode::SingleLine {
5366 cx.propagate_action();
5367 return;
5368 }
5369
5370 let text_layout_details = &self.text_layout_details(cx);
5371 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5372 let line_mode = s.line_mode;
5373 s.move_with(|map, selection| {
5374 if !selection.is_empty() && !line_mode {
5375 selection.goal = SelectionGoal::None;
5376 }
5377 let (cursor, goal) = movement::down(
5378 map,
5379 selection.end,
5380 selection.goal,
5381 false,
5382 &text_layout_details,
5383 );
5384 selection.collapse_to(cursor, goal);
5385 });
5386 });
5387 }
5388
5389 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5390 if self.take_rename(true, cx).is_some() {
5391 return;
5392 }
5393
5394 if self
5395 .context_menu
5396 .as_mut()
5397 .map(|menu| menu.select_last(cx))
5398 .unwrap_or(false)
5399 {
5400 return;
5401 }
5402
5403 if matches!(self.mode, EditorMode::SingleLine) {
5404 cx.propagate_action();
5405 return;
5406 }
5407
5408 let row_count = if let Some(row_count) = self.visible_line_count() {
5409 row_count as u32 - 1
5410 } else {
5411 return;
5412 };
5413
5414 let autoscroll = if action.center_cursor {
5415 Autoscroll::center()
5416 } else {
5417 Autoscroll::fit()
5418 };
5419
5420 let text_layout_details = &self.text_layout_details(cx);
5421 self.change_selections(Some(autoscroll), cx, |s| {
5422 let line_mode = s.line_mode;
5423 s.move_with(|map, selection| {
5424 if !selection.is_empty() && !line_mode {
5425 selection.goal = SelectionGoal::None;
5426 }
5427 let (cursor, goal) = movement::down_by_rows(
5428 map,
5429 selection.end,
5430 row_count,
5431 selection.goal,
5432 false,
5433 &text_layout_details,
5434 );
5435 selection.collapse_to(cursor, goal);
5436 });
5437 });
5438 }
5439
5440 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5441 let text_layout_details = &self.text_layout_details(cx);
5442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5443 s.move_heads_with(|map, head, goal| {
5444 movement::down(map, head, goal, false, &text_layout_details)
5445 })
5446 });
5447 }
5448
5449 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5450 if let Some(context_menu) = self.context_menu.as_mut() {
5451 context_menu.select_first(cx);
5452 }
5453 }
5454
5455 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5456 if let Some(context_menu) = self.context_menu.as_mut() {
5457 context_menu.select_prev(cx);
5458 }
5459 }
5460
5461 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5462 if let Some(context_menu) = self.context_menu.as_mut() {
5463 context_menu.select_next(cx);
5464 }
5465 }
5466
5467 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5468 if let Some(context_menu) = self.context_menu.as_mut() {
5469 context_menu.select_last(cx);
5470 }
5471 }
5472
5473 pub fn move_to_previous_word_start(
5474 &mut self,
5475 _: &MoveToPreviousWordStart,
5476 cx: &mut ViewContext<Self>,
5477 ) {
5478 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5479 s.move_cursors_with(|map, head, _| {
5480 (
5481 movement::previous_word_start(map, head),
5482 SelectionGoal::None,
5483 )
5484 });
5485 })
5486 }
5487
5488 pub fn move_to_previous_subword_start(
5489 &mut self,
5490 _: &MoveToPreviousSubwordStart,
5491 cx: &mut ViewContext<Self>,
5492 ) {
5493 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5494 s.move_cursors_with(|map, head, _| {
5495 (
5496 movement::previous_subword_start(map, head),
5497 SelectionGoal::None,
5498 )
5499 });
5500 })
5501 }
5502
5503 pub fn select_to_previous_word_start(
5504 &mut self,
5505 _: &SelectToPreviousWordStart,
5506 cx: &mut ViewContext<Self>,
5507 ) {
5508 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5509 s.move_heads_with(|map, head, _| {
5510 (
5511 movement::previous_word_start(map, head),
5512 SelectionGoal::None,
5513 )
5514 });
5515 })
5516 }
5517
5518 pub fn select_to_previous_subword_start(
5519 &mut self,
5520 _: &SelectToPreviousSubwordStart,
5521 cx: &mut ViewContext<Self>,
5522 ) {
5523 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5524 s.move_heads_with(|map, head, _| {
5525 (
5526 movement::previous_subword_start(map, head),
5527 SelectionGoal::None,
5528 )
5529 });
5530 })
5531 }
5532
5533 pub fn delete_to_previous_word_start(
5534 &mut self,
5535 _: &DeleteToPreviousWordStart,
5536 cx: &mut ViewContext<Self>,
5537 ) {
5538 self.transact(cx, |this, cx| {
5539 this.select_autoclose_pair(cx);
5540 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5541 let line_mode = s.line_mode;
5542 s.move_with(|map, selection| {
5543 if selection.is_empty() && !line_mode {
5544 let cursor = movement::previous_word_start(map, selection.head());
5545 selection.set_head(cursor, SelectionGoal::None);
5546 }
5547 });
5548 });
5549 this.insert("", cx);
5550 });
5551 }
5552
5553 pub fn delete_to_previous_subword_start(
5554 &mut self,
5555 _: &DeleteToPreviousSubwordStart,
5556 cx: &mut ViewContext<Self>,
5557 ) {
5558 self.transact(cx, |this, cx| {
5559 this.select_autoclose_pair(cx);
5560 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5561 let line_mode = s.line_mode;
5562 s.move_with(|map, selection| {
5563 if selection.is_empty() && !line_mode {
5564 let cursor = movement::previous_subword_start(map, selection.head());
5565 selection.set_head(cursor, SelectionGoal::None);
5566 }
5567 });
5568 });
5569 this.insert("", cx);
5570 });
5571 }
5572
5573 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5574 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5575 s.move_cursors_with(|map, head, _| {
5576 (movement::next_word_end(map, head), SelectionGoal::None)
5577 });
5578 })
5579 }
5580
5581 pub fn move_to_next_subword_end(
5582 &mut self,
5583 _: &MoveToNextSubwordEnd,
5584 cx: &mut ViewContext<Self>,
5585 ) {
5586 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5587 s.move_cursors_with(|map, head, _| {
5588 (movement::next_subword_end(map, head), SelectionGoal::None)
5589 });
5590 })
5591 }
5592
5593 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5594 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5595 s.move_heads_with(|map, head, _| {
5596 (movement::next_word_end(map, head), SelectionGoal::None)
5597 });
5598 })
5599 }
5600
5601 pub fn select_to_next_subword_end(
5602 &mut self,
5603 _: &SelectToNextSubwordEnd,
5604 cx: &mut ViewContext<Self>,
5605 ) {
5606 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5607 s.move_heads_with(|map, head, _| {
5608 (movement::next_subword_end(map, head), SelectionGoal::None)
5609 });
5610 })
5611 }
5612
5613 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5614 self.transact(cx, |this, cx| {
5615 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5616 let line_mode = s.line_mode;
5617 s.move_with(|map, selection| {
5618 if selection.is_empty() && !line_mode {
5619 let cursor = movement::next_word_end(map, selection.head());
5620 selection.set_head(cursor, SelectionGoal::None);
5621 }
5622 });
5623 });
5624 this.insert("", cx);
5625 });
5626 }
5627
5628 pub fn delete_to_next_subword_end(
5629 &mut self,
5630 _: &DeleteToNextSubwordEnd,
5631 cx: &mut ViewContext<Self>,
5632 ) {
5633 self.transact(cx, |this, cx| {
5634 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5635 s.move_with(|map, selection| {
5636 if selection.is_empty() {
5637 let cursor = movement::next_subword_end(map, selection.head());
5638 selection.set_head(cursor, SelectionGoal::None);
5639 }
5640 });
5641 });
5642 this.insert("", cx);
5643 });
5644 }
5645
5646 pub fn move_to_beginning_of_line(
5647 &mut self,
5648 _: &MoveToBeginningOfLine,
5649 cx: &mut ViewContext<Self>,
5650 ) {
5651 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5652 s.move_cursors_with(|map, head, _| {
5653 (
5654 movement::indented_line_beginning(map, head, true),
5655 SelectionGoal::None,
5656 )
5657 });
5658 })
5659 }
5660
5661 pub fn select_to_beginning_of_line(
5662 &mut self,
5663 action: &SelectToBeginningOfLine,
5664 cx: &mut ViewContext<Self>,
5665 ) {
5666 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5667 s.move_heads_with(|map, head, _| {
5668 (
5669 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5670 SelectionGoal::None,
5671 )
5672 });
5673 });
5674 }
5675
5676 pub fn delete_to_beginning_of_line(
5677 &mut self,
5678 _: &DeleteToBeginningOfLine,
5679 cx: &mut ViewContext<Self>,
5680 ) {
5681 self.transact(cx, |this, cx| {
5682 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5683 s.move_with(|_, selection| {
5684 selection.reversed = true;
5685 });
5686 });
5687
5688 this.select_to_beginning_of_line(
5689 &SelectToBeginningOfLine {
5690 stop_at_soft_wraps: false,
5691 },
5692 cx,
5693 );
5694 this.backspace(&Backspace, cx);
5695 });
5696 }
5697
5698 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5699 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5700 s.move_cursors_with(|map, head, _| {
5701 (movement::line_end(map, head, true), SelectionGoal::None)
5702 });
5703 })
5704 }
5705
5706 pub fn select_to_end_of_line(
5707 &mut self,
5708 action: &SelectToEndOfLine,
5709 cx: &mut ViewContext<Self>,
5710 ) {
5711 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5712 s.move_heads_with(|map, head, _| {
5713 (
5714 movement::line_end(map, head, action.stop_at_soft_wraps),
5715 SelectionGoal::None,
5716 )
5717 });
5718 })
5719 }
5720
5721 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5722 self.transact(cx, |this, cx| {
5723 this.select_to_end_of_line(
5724 &SelectToEndOfLine {
5725 stop_at_soft_wraps: false,
5726 },
5727 cx,
5728 );
5729 this.delete(&Delete, cx);
5730 });
5731 }
5732
5733 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5734 self.transact(cx, |this, cx| {
5735 this.select_to_end_of_line(
5736 &SelectToEndOfLine {
5737 stop_at_soft_wraps: false,
5738 },
5739 cx,
5740 );
5741 this.cut(&Cut, cx);
5742 });
5743 }
5744
5745 pub fn move_to_start_of_paragraph(
5746 &mut self,
5747 _: &MoveToStartOfParagraph,
5748 cx: &mut ViewContext<Self>,
5749 ) {
5750 if matches!(self.mode, EditorMode::SingleLine) {
5751 cx.propagate_action();
5752 return;
5753 }
5754
5755 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5756 s.move_with(|map, selection| {
5757 selection.collapse_to(
5758 movement::start_of_paragraph(map, selection.head(), 1),
5759 SelectionGoal::None,
5760 )
5761 });
5762 })
5763 }
5764
5765 pub fn move_to_end_of_paragraph(
5766 &mut self,
5767 _: &MoveToEndOfParagraph,
5768 cx: &mut ViewContext<Self>,
5769 ) {
5770 if matches!(self.mode, EditorMode::SingleLine) {
5771 cx.propagate_action();
5772 return;
5773 }
5774
5775 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5776 s.move_with(|map, selection| {
5777 selection.collapse_to(
5778 movement::end_of_paragraph(map, selection.head(), 1),
5779 SelectionGoal::None,
5780 )
5781 });
5782 })
5783 }
5784
5785 pub fn select_to_start_of_paragraph(
5786 &mut self,
5787 _: &SelectToStartOfParagraph,
5788 cx: &mut ViewContext<Self>,
5789 ) {
5790 if matches!(self.mode, EditorMode::SingleLine) {
5791 cx.propagate_action();
5792 return;
5793 }
5794
5795 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5796 s.move_heads_with(|map, head, _| {
5797 (
5798 movement::start_of_paragraph(map, head, 1),
5799 SelectionGoal::None,
5800 )
5801 });
5802 })
5803 }
5804
5805 pub fn select_to_end_of_paragraph(
5806 &mut self,
5807 _: &SelectToEndOfParagraph,
5808 cx: &mut ViewContext<Self>,
5809 ) {
5810 if matches!(self.mode, EditorMode::SingleLine) {
5811 cx.propagate_action();
5812 return;
5813 }
5814
5815 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5816 s.move_heads_with(|map, head, _| {
5817 (
5818 movement::end_of_paragraph(map, head, 1),
5819 SelectionGoal::None,
5820 )
5821 });
5822 })
5823 }
5824
5825 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5826 if matches!(self.mode, EditorMode::SingleLine) {
5827 cx.propagate_action();
5828 return;
5829 }
5830
5831 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5832 s.select_ranges(vec![0..0]);
5833 });
5834 }
5835
5836 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5837 let mut selection = self.selections.last::<Point>(cx);
5838 selection.set_head(Point::zero(), SelectionGoal::None);
5839
5840 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5841 s.select(vec![selection]);
5842 });
5843 }
5844
5845 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5846 if matches!(self.mode, EditorMode::SingleLine) {
5847 cx.propagate_action();
5848 return;
5849 }
5850
5851 let cursor = self.buffer.read(cx).read(cx).len();
5852 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5853 s.select_ranges(vec![cursor..cursor])
5854 });
5855 }
5856
5857 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5858 self.nav_history = nav_history;
5859 }
5860
5861 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5862 self.nav_history.as_ref()
5863 }
5864
5865 fn push_to_nav_history(
5866 &mut self,
5867 cursor_anchor: Anchor,
5868 new_position: Option<Point>,
5869 cx: &mut ViewContext<Self>,
5870 ) {
5871 if let Some(nav_history) = self.nav_history.as_mut() {
5872 let buffer = self.buffer.read(cx).read(cx);
5873 let cursor_position = cursor_anchor.to_point(&buffer);
5874 let scroll_state = self.scroll_manager.anchor();
5875 let scroll_top_row = scroll_state.top_row(&buffer);
5876 drop(buffer);
5877
5878 if let Some(new_position) = new_position {
5879 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5880 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5881 return;
5882 }
5883 }
5884
5885 nav_history.push(
5886 Some(NavigationData {
5887 cursor_anchor,
5888 cursor_position,
5889 scroll_anchor: scroll_state,
5890 scroll_top_row,
5891 }),
5892 cx,
5893 );
5894 }
5895 }
5896
5897 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5898 let buffer = self.buffer.read(cx).snapshot(cx);
5899 let mut selection = self.selections.first::<usize>(cx);
5900 selection.set_head(buffer.len(), SelectionGoal::None);
5901 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5902 s.select(vec![selection]);
5903 });
5904 }
5905
5906 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5907 let end = self.buffer.read(cx).read(cx).len();
5908 self.change_selections(None, cx, |s| {
5909 s.select_ranges(vec![0..end]);
5910 });
5911 }
5912
5913 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5914 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5915 let mut selections = self.selections.all::<Point>(cx);
5916 let max_point = display_map.buffer_snapshot.max_point();
5917 for selection in &mut selections {
5918 let rows = selection.spanned_rows(true, &display_map);
5919 selection.start = Point::new(rows.start, 0);
5920 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5921 selection.reversed = false;
5922 }
5923 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5924 s.select(selections);
5925 });
5926 }
5927
5928 pub fn split_selection_into_lines(
5929 &mut self,
5930 _: &SplitSelectionIntoLines,
5931 cx: &mut ViewContext<Self>,
5932 ) {
5933 let mut to_unfold = Vec::new();
5934 let mut new_selection_ranges = Vec::new();
5935 {
5936 let selections = self.selections.all::<Point>(cx);
5937 let buffer = self.buffer.read(cx).read(cx);
5938 for selection in selections {
5939 for row in selection.start.row..selection.end.row {
5940 let cursor = Point::new(row, buffer.line_len(row));
5941 new_selection_ranges.push(cursor..cursor);
5942 }
5943 new_selection_ranges.push(selection.end..selection.end);
5944 to_unfold.push(selection.start..selection.end);
5945 }
5946 }
5947 self.unfold_ranges(to_unfold, true, true, cx);
5948 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5949 s.select_ranges(new_selection_ranges);
5950 });
5951 }
5952
5953 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5954 self.add_selection(true, cx);
5955 }
5956
5957 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5958 self.add_selection(false, cx);
5959 }
5960
5961 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5962 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5963 let mut selections = self.selections.all::<Point>(cx);
5964 let text_layout_details = self.text_layout_details(cx);
5965 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5966 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5967 let range = oldest_selection.display_range(&display_map).sorted();
5968
5969 let start_x = display_map.x_for_point(range.start, &text_layout_details);
5970 let end_x = display_map.x_for_point(range.end, &text_layout_details);
5971 let positions = start_x.min(end_x)..start_x.max(end_x);
5972
5973 selections.clear();
5974 let mut stack = Vec::new();
5975 for row in range.start.row()..=range.end.row() {
5976 if let Some(selection) = self.selections.build_columnar_selection(
5977 &display_map,
5978 row,
5979 &positions,
5980 oldest_selection.reversed,
5981 &text_layout_details,
5982 ) {
5983 stack.push(selection.id);
5984 selections.push(selection);
5985 }
5986 }
5987
5988 if above {
5989 stack.reverse();
5990 }
5991
5992 AddSelectionsState { above, stack }
5993 });
5994
5995 let last_added_selection = *state.stack.last().unwrap();
5996 let mut new_selections = Vec::new();
5997 if above == state.above {
5998 let end_row = if above {
5999 0
6000 } else {
6001 display_map.max_point().row()
6002 };
6003
6004 'outer: for selection in selections {
6005 if selection.id == last_added_selection {
6006 let range = selection.display_range(&display_map).sorted();
6007 debug_assert_eq!(range.start.row(), range.end.row());
6008 let mut row = range.start.row();
6009 let positions = if let SelectionGoal::HorizontalRange { start, end } =
6010 selection.goal
6011 {
6012 start..end
6013 } else {
6014 let start_x = display_map.x_for_point(range.start, &text_layout_details);
6015 let end_x = display_map.x_for_point(range.end, &text_layout_details);
6016
6017 start_x.min(end_x)..start_x.max(end_x)
6018 };
6019
6020 while row != end_row {
6021 if above {
6022 row -= 1;
6023 } else {
6024 row += 1;
6025 }
6026
6027 if let Some(new_selection) = self.selections.build_columnar_selection(
6028 &display_map,
6029 row,
6030 &positions,
6031 selection.reversed,
6032 &text_layout_details,
6033 ) {
6034 state.stack.push(new_selection.id);
6035 if above {
6036 new_selections.push(new_selection);
6037 new_selections.push(selection);
6038 } else {
6039 new_selections.push(selection);
6040 new_selections.push(new_selection);
6041 }
6042
6043 continue 'outer;
6044 }
6045 }
6046 }
6047
6048 new_selections.push(selection);
6049 }
6050 } else {
6051 new_selections = selections;
6052 new_selections.retain(|s| s.id != last_added_selection);
6053 state.stack.pop();
6054 }
6055
6056 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6057 s.select(new_selections);
6058 });
6059 if state.stack.len() > 1 {
6060 self.add_selections_state = Some(state);
6061 }
6062 }
6063
6064 pub fn select_next_match_internal(
6065 &mut self,
6066 display_map: &DisplaySnapshot,
6067 replace_newest: bool,
6068 autoscroll: Option<Autoscroll>,
6069 cx: &mut ViewContext<Self>,
6070 ) -> Result<()> {
6071 fn select_next_match_ranges(
6072 this: &mut Editor,
6073 range: Range<usize>,
6074 replace_newest: bool,
6075 auto_scroll: Option<Autoscroll>,
6076 cx: &mut ViewContext<Editor>,
6077 ) {
6078 this.unfold_ranges([range.clone()], false, true, cx);
6079 this.change_selections(auto_scroll, cx, |s| {
6080 if replace_newest {
6081 s.delete(s.newest_anchor().id);
6082 }
6083 s.insert_range(range.clone());
6084 });
6085 }
6086
6087 let buffer = &display_map.buffer_snapshot;
6088 let mut selections = self.selections.all::<usize>(cx);
6089 if let Some(mut select_next_state) = self.select_next_state.take() {
6090 let query = &select_next_state.query;
6091 if !select_next_state.done {
6092 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6093 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6094 let mut next_selected_range = None;
6095
6096 let bytes_after_last_selection =
6097 buffer.bytes_in_range(last_selection.end..buffer.len());
6098 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6099 let query_matches = query
6100 .stream_find_iter(bytes_after_last_selection)
6101 .map(|result| (last_selection.end, result))
6102 .chain(
6103 query
6104 .stream_find_iter(bytes_before_first_selection)
6105 .map(|result| (0, result)),
6106 );
6107
6108 for (start_offset, query_match) in query_matches {
6109 let query_match = query_match.unwrap(); // can only fail due to I/O
6110 let offset_range =
6111 start_offset + query_match.start()..start_offset + query_match.end();
6112 let display_range = offset_range.start.to_display_point(&display_map)
6113 ..offset_range.end.to_display_point(&display_map);
6114
6115 if !select_next_state.wordwise
6116 || (!movement::is_inside_word(&display_map, display_range.start)
6117 && !movement::is_inside_word(&display_map, display_range.end))
6118 {
6119 if selections
6120 .iter()
6121 .find(|selection| selection.equals(&offset_range))
6122 .is_none()
6123 {
6124 next_selected_range = Some(offset_range);
6125 break;
6126 }
6127 }
6128 }
6129
6130 if let Some(next_selected_range) = next_selected_range {
6131 select_next_match_ranges(
6132 self,
6133 next_selected_range,
6134 replace_newest,
6135 autoscroll,
6136 cx,
6137 );
6138 } else {
6139 select_next_state.done = true;
6140 }
6141 }
6142
6143 self.select_next_state = Some(select_next_state);
6144 } else if selections.len() == 1 {
6145 let selection = selections.last_mut().unwrap();
6146 if selection.start == selection.end {
6147 let word_range = movement::surrounding_word(
6148 &display_map,
6149 selection.start.to_display_point(&display_map),
6150 );
6151 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6152 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6153 selection.goal = SelectionGoal::None;
6154 selection.reversed = false;
6155
6156 let query = buffer
6157 .text_for_range(selection.start..selection.end)
6158 .collect::<String>();
6159
6160 let is_empty = query.is_empty();
6161 let select_state = SelectNextState {
6162 query: AhoCorasick::new(&[query])?,
6163 wordwise: true,
6164 done: is_empty,
6165 };
6166 select_next_match_ranges(
6167 self,
6168 selection.start..selection.end,
6169 replace_newest,
6170 autoscroll,
6171 cx,
6172 );
6173 self.select_next_state = Some(select_state);
6174 } else {
6175 let query = buffer
6176 .text_for_range(selection.start..selection.end)
6177 .collect::<String>();
6178 self.select_next_state = Some(SelectNextState {
6179 query: AhoCorasick::new(&[query])?,
6180 wordwise: false,
6181 done: false,
6182 });
6183 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6184 }
6185 }
6186 Ok(())
6187 }
6188
6189 pub fn select_all_matches(
6190 &mut self,
6191 action: &SelectAllMatches,
6192 cx: &mut ViewContext<Self>,
6193 ) -> Result<()> {
6194 self.push_to_selection_history();
6195 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6196
6197 loop {
6198 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6199
6200 if self
6201 .select_next_state
6202 .as_ref()
6203 .map(|selection_state| selection_state.done)
6204 .unwrap_or(true)
6205 {
6206 break;
6207 }
6208 }
6209
6210 Ok(())
6211 }
6212
6213 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6214 self.push_to_selection_history();
6215 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6216 self.select_next_match_internal(
6217 &display_map,
6218 action.replace_newest,
6219 Some(Autoscroll::newest()),
6220 cx,
6221 )?;
6222 Ok(())
6223 }
6224
6225 pub fn select_previous(
6226 &mut self,
6227 action: &SelectPrevious,
6228 cx: &mut ViewContext<Self>,
6229 ) -> Result<()> {
6230 self.push_to_selection_history();
6231 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6232 let buffer = &display_map.buffer_snapshot;
6233 let mut selections = self.selections.all::<usize>(cx);
6234 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6235 let query = &select_prev_state.query;
6236 if !select_prev_state.done {
6237 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6238 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6239 let mut next_selected_range = None;
6240 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6241 let bytes_before_last_selection =
6242 buffer.reversed_bytes_in_range(0..last_selection.start);
6243 let bytes_after_first_selection =
6244 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6245 let query_matches = query
6246 .stream_find_iter(bytes_before_last_selection)
6247 .map(|result| (last_selection.start, result))
6248 .chain(
6249 query
6250 .stream_find_iter(bytes_after_first_selection)
6251 .map(|result| (buffer.len(), result)),
6252 );
6253 for (end_offset, query_match) in query_matches {
6254 let query_match = query_match.unwrap(); // can only fail due to I/O
6255 let offset_range =
6256 end_offset - query_match.end()..end_offset - query_match.start();
6257 let display_range = offset_range.start.to_display_point(&display_map)
6258 ..offset_range.end.to_display_point(&display_map);
6259
6260 if !select_prev_state.wordwise
6261 || (!movement::is_inside_word(&display_map, display_range.start)
6262 && !movement::is_inside_word(&display_map, display_range.end))
6263 {
6264 next_selected_range = Some(offset_range);
6265 break;
6266 }
6267 }
6268
6269 if let Some(next_selected_range) = next_selected_range {
6270 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6271 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6272 if action.replace_newest {
6273 s.delete(s.newest_anchor().id);
6274 }
6275 s.insert_range(next_selected_range);
6276 });
6277 } else {
6278 select_prev_state.done = true;
6279 }
6280 }
6281
6282 self.select_prev_state = Some(select_prev_state);
6283 } else if selections.len() == 1 {
6284 let selection = selections.last_mut().unwrap();
6285 if selection.start == selection.end {
6286 let word_range = movement::surrounding_word(
6287 &display_map,
6288 selection.start.to_display_point(&display_map),
6289 );
6290 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6291 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6292 selection.goal = SelectionGoal::None;
6293 selection.reversed = false;
6294
6295 let query = buffer
6296 .text_for_range(selection.start..selection.end)
6297 .collect::<String>();
6298 let query = query.chars().rev().collect::<String>();
6299 let select_state = SelectNextState {
6300 query: AhoCorasick::new(&[query])?,
6301 wordwise: true,
6302 done: false,
6303 };
6304 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6305 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6306 s.select(selections);
6307 });
6308 self.select_prev_state = Some(select_state);
6309 } else {
6310 let query = buffer
6311 .text_for_range(selection.start..selection.end)
6312 .collect::<String>();
6313 let query = query.chars().rev().collect::<String>();
6314 self.select_prev_state = Some(SelectNextState {
6315 query: AhoCorasick::new(&[query])?,
6316 wordwise: false,
6317 done: false,
6318 });
6319 self.select_previous(action, cx)?;
6320 }
6321 }
6322 Ok(())
6323 }
6324
6325 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6326 let text_layout_details = &self.text_layout_details(cx);
6327 self.transact(cx, |this, cx| {
6328 let mut selections = this.selections.all::<Point>(cx);
6329 let mut edits = Vec::new();
6330 let mut selection_edit_ranges = Vec::new();
6331 let mut last_toggled_row = None;
6332 let snapshot = this.buffer.read(cx).read(cx);
6333 let empty_str: Arc<str> = "".into();
6334 let mut suffixes_inserted = Vec::new();
6335
6336 fn comment_prefix_range(
6337 snapshot: &MultiBufferSnapshot,
6338 row: u32,
6339 comment_prefix: &str,
6340 comment_prefix_whitespace: &str,
6341 ) -> Range<Point> {
6342 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6343
6344 let mut line_bytes = snapshot
6345 .bytes_in_range(start..snapshot.max_point())
6346 .flatten()
6347 .copied();
6348
6349 // If this line currently begins with the line comment prefix, then record
6350 // the range containing the prefix.
6351 if line_bytes
6352 .by_ref()
6353 .take(comment_prefix.len())
6354 .eq(comment_prefix.bytes())
6355 {
6356 // Include any whitespace that matches the comment prefix.
6357 let matching_whitespace_len = line_bytes
6358 .zip(comment_prefix_whitespace.bytes())
6359 .take_while(|(a, b)| a == b)
6360 .count() as u32;
6361 let end = Point::new(
6362 start.row,
6363 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6364 );
6365 start..end
6366 } else {
6367 start..start
6368 }
6369 }
6370
6371 fn comment_suffix_range(
6372 snapshot: &MultiBufferSnapshot,
6373 row: u32,
6374 comment_suffix: &str,
6375 comment_suffix_has_leading_space: bool,
6376 ) -> Range<Point> {
6377 let end = Point::new(row, snapshot.line_len(row));
6378 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6379
6380 let mut line_end_bytes = snapshot
6381 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6382 .flatten()
6383 .copied();
6384
6385 let leading_space_len = if suffix_start_column > 0
6386 && line_end_bytes.next() == Some(b' ')
6387 && comment_suffix_has_leading_space
6388 {
6389 1
6390 } else {
6391 0
6392 };
6393
6394 // If this line currently begins with the line comment prefix, then record
6395 // the range containing the prefix.
6396 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6397 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6398 start..end
6399 } else {
6400 end..end
6401 }
6402 }
6403
6404 // TODO: Handle selections that cross excerpts
6405 for selection in &mut selections {
6406 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6407 let language = if let Some(language) =
6408 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6409 {
6410 language
6411 } else {
6412 continue;
6413 };
6414
6415 selection_edit_ranges.clear();
6416
6417 // If multiple selections contain a given row, avoid processing that
6418 // row more than once.
6419 let mut start_row = selection.start.row;
6420 if last_toggled_row == Some(start_row) {
6421 start_row += 1;
6422 }
6423 let end_row =
6424 if selection.end.row > selection.start.row && selection.end.column == 0 {
6425 selection.end.row - 1
6426 } else {
6427 selection.end.row
6428 };
6429 last_toggled_row = Some(end_row);
6430
6431 if start_row > end_row {
6432 continue;
6433 }
6434
6435 // If the language has line comments, toggle those.
6436 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6437 // Split the comment prefix's trailing whitespace into a separate string,
6438 // as that portion won't be used for detecting if a line is a comment.
6439 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6440 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6441 let mut all_selection_lines_are_comments = true;
6442
6443 for row in start_row..=end_row {
6444 if snapshot.is_line_blank(row) && start_row < end_row {
6445 continue;
6446 }
6447
6448 let prefix_range = comment_prefix_range(
6449 snapshot.deref(),
6450 row,
6451 comment_prefix,
6452 comment_prefix_whitespace,
6453 );
6454 if prefix_range.is_empty() {
6455 all_selection_lines_are_comments = false;
6456 }
6457 selection_edit_ranges.push(prefix_range);
6458 }
6459
6460 if all_selection_lines_are_comments {
6461 edits.extend(
6462 selection_edit_ranges
6463 .iter()
6464 .cloned()
6465 .map(|range| (range, empty_str.clone())),
6466 );
6467 } else {
6468 let min_column = selection_edit_ranges
6469 .iter()
6470 .map(|r| r.start.column)
6471 .min()
6472 .unwrap_or(0);
6473 edits.extend(selection_edit_ranges.iter().map(|range| {
6474 let position = Point::new(range.start.row, min_column);
6475 (position..position, full_comment_prefix.clone())
6476 }));
6477 }
6478 } else if let Some((full_comment_prefix, comment_suffix)) =
6479 language.block_comment_delimiters()
6480 {
6481 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6482 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6483 let prefix_range = comment_prefix_range(
6484 snapshot.deref(),
6485 start_row,
6486 comment_prefix,
6487 comment_prefix_whitespace,
6488 );
6489 let suffix_range = comment_suffix_range(
6490 snapshot.deref(),
6491 end_row,
6492 comment_suffix.trim_start_matches(' '),
6493 comment_suffix.starts_with(' '),
6494 );
6495
6496 if prefix_range.is_empty() || suffix_range.is_empty() {
6497 edits.push((
6498 prefix_range.start..prefix_range.start,
6499 full_comment_prefix.clone(),
6500 ));
6501 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6502 suffixes_inserted.push((end_row, comment_suffix.len()));
6503 } else {
6504 edits.push((prefix_range, empty_str.clone()));
6505 edits.push((suffix_range, empty_str.clone()));
6506 }
6507 } else {
6508 continue;
6509 }
6510 }
6511
6512 drop(snapshot);
6513 this.buffer.update(cx, |buffer, cx| {
6514 buffer.edit(edits, None, cx);
6515 });
6516
6517 // Adjust selections so that they end before any comment suffixes that
6518 // were inserted.
6519 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6520 let mut selections = this.selections.all::<Point>(cx);
6521 let snapshot = this.buffer.read(cx).read(cx);
6522 for selection in &mut selections {
6523 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6524 match row.cmp(&selection.end.row) {
6525 Ordering::Less => {
6526 suffixes_inserted.next();
6527 continue;
6528 }
6529 Ordering::Greater => break,
6530 Ordering::Equal => {
6531 if selection.end.column == snapshot.line_len(row) {
6532 if selection.is_empty() {
6533 selection.start.column -= suffix_len as u32;
6534 }
6535 selection.end.column -= suffix_len as u32;
6536 }
6537 break;
6538 }
6539 }
6540 }
6541 }
6542
6543 drop(snapshot);
6544 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6545
6546 let selections = this.selections.all::<Point>(cx);
6547 let selections_on_single_row = selections.windows(2).all(|selections| {
6548 selections[0].start.row == selections[1].start.row
6549 && selections[0].end.row == selections[1].end.row
6550 && selections[0].start.row == selections[0].end.row
6551 });
6552 let selections_selecting = selections
6553 .iter()
6554 .any(|selection| selection.start != selection.end);
6555 let advance_downwards = action.advance_downwards
6556 && selections_on_single_row
6557 && !selections_selecting
6558 && this.mode != EditorMode::SingleLine;
6559
6560 if advance_downwards {
6561 let snapshot = this.buffer.read(cx).snapshot(cx);
6562
6563 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6564 s.move_cursors_with(|display_snapshot, display_point, _| {
6565 let mut point = display_point.to_point(display_snapshot);
6566 point.row += 1;
6567 point = snapshot.clip_point(point, Bias::Left);
6568 let display_point = point.to_display_point(display_snapshot);
6569 let goal = SelectionGoal::HorizontalPosition(
6570 display_snapshot.x_for_point(display_point, &text_layout_details),
6571 );
6572 (display_point, goal)
6573 })
6574 });
6575 }
6576 });
6577 }
6578
6579 pub fn select_larger_syntax_node(
6580 &mut self,
6581 _: &SelectLargerSyntaxNode,
6582 cx: &mut ViewContext<Self>,
6583 ) {
6584 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6585 let buffer = self.buffer.read(cx).snapshot(cx);
6586 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6587
6588 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6589 let mut selected_larger_node = false;
6590 let new_selections = old_selections
6591 .iter()
6592 .map(|selection| {
6593 let old_range = selection.start..selection.end;
6594 let mut new_range = old_range.clone();
6595 while let Some(containing_range) =
6596 buffer.range_for_syntax_ancestor(new_range.clone())
6597 {
6598 new_range = containing_range;
6599 if !display_map.intersects_fold(new_range.start)
6600 && !display_map.intersects_fold(new_range.end)
6601 {
6602 break;
6603 }
6604 }
6605
6606 selected_larger_node |= new_range != old_range;
6607 Selection {
6608 id: selection.id,
6609 start: new_range.start,
6610 end: new_range.end,
6611 goal: SelectionGoal::None,
6612 reversed: selection.reversed,
6613 }
6614 })
6615 .collect::<Vec<_>>();
6616
6617 if selected_larger_node {
6618 stack.push(old_selections);
6619 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6620 s.select(new_selections);
6621 });
6622 }
6623 self.select_larger_syntax_node_stack = stack;
6624 }
6625
6626 pub fn select_smaller_syntax_node(
6627 &mut self,
6628 _: &SelectSmallerSyntaxNode,
6629 cx: &mut ViewContext<Self>,
6630 ) {
6631 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6632 if let Some(selections) = stack.pop() {
6633 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6634 s.select(selections.to_vec());
6635 });
6636 }
6637 self.select_larger_syntax_node_stack = stack;
6638 }
6639
6640 pub fn move_to_enclosing_bracket(
6641 &mut self,
6642 _: &MoveToEnclosingBracket,
6643 cx: &mut ViewContext<Self>,
6644 ) {
6645 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6646 s.move_offsets_with(|snapshot, selection| {
6647 let Some(enclosing_bracket_ranges) =
6648 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6649 else {
6650 return;
6651 };
6652
6653 let mut best_length = usize::MAX;
6654 let mut best_inside = false;
6655 let mut best_in_bracket_range = false;
6656 let mut best_destination = None;
6657 for (open, close) in enclosing_bracket_ranges {
6658 let close = close.to_inclusive();
6659 let length = close.end() - open.start;
6660 let inside = selection.start >= open.end && selection.end <= *close.start();
6661 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6662 || close.contains(&selection.head());
6663
6664 // If best is next to a bracket and current isn't, skip
6665 if !in_bracket_range && best_in_bracket_range {
6666 continue;
6667 }
6668
6669 // Prefer smaller lengths unless best is inside and current isn't
6670 if length > best_length && (best_inside || !inside) {
6671 continue;
6672 }
6673
6674 best_length = length;
6675 best_inside = inside;
6676 best_in_bracket_range = in_bracket_range;
6677 best_destination = Some(
6678 if close.contains(&selection.start) && close.contains(&selection.end) {
6679 if inside {
6680 open.end
6681 } else {
6682 open.start
6683 }
6684 } else {
6685 if inside {
6686 *close.start()
6687 } else {
6688 *close.end()
6689 }
6690 },
6691 );
6692 }
6693
6694 if let Some(destination) = best_destination {
6695 selection.collapse_to(destination, SelectionGoal::None);
6696 }
6697 })
6698 });
6699 }
6700
6701 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6702 self.end_selection(cx);
6703 self.selection_history.mode = SelectionHistoryMode::Undoing;
6704 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6705 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6706 self.select_next_state = entry.select_next_state;
6707 self.select_prev_state = entry.select_prev_state;
6708 self.add_selections_state = entry.add_selections_state;
6709 self.request_autoscroll(Autoscroll::newest(), cx);
6710 }
6711 self.selection_history.mode = SelectionHistoryMode::Normal;
6712 }
6713
6714 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6715 self.end_selection(cx);
6716 self.selection_history.mode = SelectionHistoryMode::Redoing;
6717 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6718 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6719 self.select_next_state = entry.select_next_state;
6720 self.select_prev_state = entry.select_prev_state;
6721 self.add_selections_state = entry.add_selections_state;
6722 self.request_autoscroll(Autoscroll::newest(), cx);
6723 }
6724 self.selection_history.mode = SelectionHistoryMode::Normal;
6725 }
6726
6727 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6728 self.go_to_diagnostic_impl(Direction::Next, cx)
6729 }
6730
6731 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6732 self.go_to_diagnostic_impl(Direction::Prev, cx)
6733 }
6734
6735 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6736 let buffer = self.buffer.read(cx).snapshot(cx);
6737 let selection = self.selections.newest::<usize>(cx);
6738
6739 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6740 if direction == Direction::Next {
6741 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6742 let (group_id, jump_to) = popover.activation_info();
6743 if self.activate_diagnostics(group_id, cx) {
6744 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6745 let mut new_selection = s.newest_anchor().clone();
6746 new_selection.collapse_to(jump_to, SelectionGoal::None);
6747 s.select_anchors(vec![new_selection.clone()]);
6748 });
6749 }
6750 return;
6751 }
6752 }
6753
6754 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6755 active_diagnostics
6756 .primary_range
6757 .to_offset(&buffer)
6758 .to_inclusive()
6759 });
6760 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6761 if active_primary_range.contains(&selection.head()) {
6762 *active_primary_range.end()
6763 } else {
6764 selection.head()
6765 }
6766 } else {
6767 selection.head()
6768 };
6769
6770 loop {
6771 let mut diagnostics = if direction == Direction::Prev {
6772 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6773 } else {
6774 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6775 };
6776 let group = diagnostics.find_map(|entry| {
6777 if entry.diagnostic.is_primary
6778 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6779 && !entry.range.is_empty()
6780 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6781 {
6782 Some((entry.range, entry.diagnostic.group_id))
6783 } else {
6784 None
6785 }
6786 });
6787
6788 if let Some((primary_range, group_id)) = group {
6789 if self.activate_diagnostics(group_id, cx) {
6790 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6791 s.select(vec![Selection {
6792 id: selection.id,
6793 start: primary_range.start,
6794 end: primary_range.start,
6795 reversed: false,
6796 goal: SelectionGoal::None,
6797 }]);
6798 });
6799 }
6800 break;
6801 } else {
6802 // Cycle around to the start of the buffer, potentially moving back to the start of
6803 // the currently active diagnostic.
6804 active_primary_range.take();
6805 if direction == Direction::Prev {
6806 if search_start == buffer.len() {
6807 break;
6808 } else {
6809 search_start = buffer.len();
6810 }
6811 } else if search_start == 0 {
6812 break;
6813 } else {
6814 search_start = 0;
6815 }
6816 }
6817 }
6818 }
6819
6820 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6821 let snapshot = self
6822 .display_map
6823 .update(cx, |display_map, cx| display_map.snapshot(cx));
6824 let selection = self.selections.newest::<Point>(cx);
6825
6826 if !self.seek_in_direction(
6827 &snapshot,
6828 selection.head(),
6829 false,
6830 snapshot
6831 .buffer_snapshot
6832 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6833 cx,
6834 ) {
6835 let wrapped_point = Point::zero();
6836 self.seek_in_direction(
6837 &snapshot,
6838 wrapped_point,
6839 true,
6840 snapshot
6841 .buffer_snapshot
6842 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6843 cx,
6844 );
6845 }
6846 }
6847
6848 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6849 let snapshot = self
6850 .display_map
6851 .update(cx, |display_map, cx| display_map.snapshot(cx));
6852 let selection = self.selections.newest::<Point>(cx);
6853
6854 if !self.seek_in_direction(
6855 &snapshot,
6856 selection.head(),
6857 false,
6858 snapshot
6859 .buffer_snapshot
6860 .git_diff_hunks_in_range_rev(0..selection.head().row),
6861 cx,
6862 ) {
6863 let wrapped_point = snapshot.buffer_snapshot.max_point();
6864 self.seek_in_direction(
6865 &snapshot,
6866 wrapped_point,
6867 true,
6868 snapshot
6869 .buffer_snapshot
6870 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6871 cx,
6872 );
6873 }
6874 }
6875
6876 fn seek_in_direction(
6877 &mut self,
6878 snapshot: &DisplaySnapshot,
6879 initial_point: Point,
6880 is_wrapped: bool,
6881 hunks: impl Iterator<Item = DiffHunk<u32>>,
6882 cx: &mut ViewContext<Editor>,
6883 ) -> bool {
6884 let display_point = initial_point.to_display_point(snapshot);
6885 let mut hunks = hunks
6886 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6887 .skip_while(|hunk| {
6888 if is_wrapped {
6889 false
6890 } else {
6891 hunk.contains_display_row(display_point.row())
6892 }
6893 })
6894 .dedup();
6895
6896 if let Some(hunk) = hunks.next() {
6897 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6898 let row = hunk.start_display_row();
6899 let point = DisplayPoint::new(row, 0);
6900 s.select_display_ranges([point..point]);
6901 });
6902
6903 true
6904 } else {
6905 false
6906 }
6907 }
6908
6909 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6910 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6911 }
6912
6913 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6914 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6915 }
6916
6917 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6918 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6919 }
6920
6921 pub fn go_to_type_definition_split(
6922 &mut self,
6923 _: &GoToTypeDefinitionSplit,
6924 cx: &mut ViewContext<Self>,
6925 ) {
6926 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6927 }
6928
6929 fn go_to_definition_of_kind(
6930 &mut self,
6931 kind: GotoDefinitionKind,
6932 split: bool,
6933 cx: &mut ViewContext<Self>,
6934 ) {
6935 let Some(workspace) = self.workspace(cx) else {
6936 return;
6937 };
6938 let buffer = self.buffer.read(cx);
6939 let head = self.selections.newest::<usize>(cx).head();
6940 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6941 text_anchor
6942 } else {
6943 return;
6944 };
6945
6946 let project = workspace.read(cx).project().clone();
6947 let definitions = project.update(cx, |project, cx| match kind {
6948 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6949 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6950 });
6951
6952 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6953 let definitions = definitions.await?;
6954 editor.update(&mut cx, |editor, cx| {
6955 editor.navigate_to_definitions(
6956 definitions
6957 .into_iter()
6958 .map(GoToDefinitionLink::Text)
6959 .collect(),
6960 split,
6961 cx,
6962 );
6963 })?;
6964 Ok::<(), anyhow::Error>(())
6965 })
6966 .detach_and_log_err(cx);
6967 }
6968
6969 pub fn navigate_to_definitions(
6970 &mut self,
6971 mut definitions: Vec<GoToDefinitionLink>,
6972 split: bool,
6973 cx: &mut ViewContext<Editor>,
6974 ) {
6975 let Some(workspace) = self.workspace(cx) else {
6976 return;
6977 };
6978 let pane = workspace.read(cx).active_pane().clone();
6979 // If there is one definition, just open it directly
6980 if definitions.len() == 1 {
6981 let definition = definitions.pop().unwrap();
6982 let target_task = match definition {
6983 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
6984 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6985 self.compute_target_location(lsp_location, server_id, cx)
6986 }
6987 };
6988 cx.spawn(|editor, mut cx| async move {
6989 let target = target_task.await.context("target resolution task")?;
6990 if let Some(target) = target {
6991 editor.update(&mut cx, |editor, cx| {
6992 let range = target.range.to_offset(target.buffer.read(cx));
6993 let range = editor.range_for_match(&range);
6994 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
6995 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6996 s.select_ranges([range]);
6997 });
6998 } else {
6999 cx.window_context().defer(move |cx| {
7000 let target_editor: ViewHandle<Self> =
7001 workspace.update(cx, |workspace, cx| {
7002 if split {
7003 workspace.split_project_item(target.buffer.clone(), cx)
7004 } else {
7005 workspace.open_project_item(target.buffer.clone(), cx)
7006 }
7007 });
7008 target_editor.update(cx, |target_editor, cx| {
7009 // When selecting a definition in a different buffer, disable the nav history
7010 // to avoid creating a history entry at the previous cursor location.
7011 pane.update(cx, |pane, _| pane.disable_history());
7012 target_editor.change_selections(
7013 Some(Autoscroll::fit()),
7014 cx,
7015 |s| {
7016 s.select_ranges([range]);
7017 },
7018 );
7019 pane.update(cx, |pane, _| pane.enable_history());
7020 });
7021 });
7022 }
7023 })
7024 } else {
7025 Ok(())
7026 }
7027 })
7028 .detach_and_log_err(cx);
7029 } else if !definitions.is_empty() {
7030 let replica_id = self.replica_id(cx);
7031 cx.spawn(|editor, mut cx| async move {
7032 let (title, location_tasks) = editor
7033 .update(&mut cx, |editor, cx| {
7034 let title = definitions
7035 .iter()
7036 .find_map(|definition| match definition {
7037 GoToDefinitionLink::Text(link) => {
7038 link.origin.as_ref().map(|origin| {
7039 let buffer = origin.buffer.read(cx);
7040 format!(
7041 "Definitions for {}",
7042 buffer
7043 .text_for_range(origin.range.clone())
7044 .collect::<String>()
7045 )
7046 })
7047 }
7048 GoToDefinitionLink::InlayHint(_, _) => None,
7049 })
7050 .unwrap_or("Definitions".to_string());
7051 let location_tasks = definitions
7052 .into_iter()
7053 .map(|definition| match definition {
7054 GoToDefinitionLink::Text(link) => {
7055 Task::Ready(Some(Ok(Some(link.target))))
7056 }
7057 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
7058 editor.compute_target_location(lsp_location, server_id, cx)
7059 }
7060 })
7061 .collect::<Vec<_>>();
7062 (title, location_tasks)
7063 })
7064 .context("location tasks preparation")?;
7065
7066 let locations = futures::future::join_all(location_tasks)
7067 .await
7068 .into_iter()
7069 .filter_map(|location| location.transpose())
7070 .collect::<Result<_>>()
7071 .context("location tasks")?;
7072 workspace.update(&mut cx, |workspace, cx| {
7073 Self::open_locations_in_multibuffer(
7074 workspace, locations, replica_id, title, split, cx,
7075 )
7076 });
7077
7078 anyhow::Ok(())
7079 })
7080 .detach_and_log_err(cx);
7081 }
7082 }
7083
7084 fn compute_target_location(
7085 &self,
7086 lsp_location: lsp::Location,
7087 server_id: LanguageServerId,
7088 cx: &mut ViewContext<Editor>,
7089 ) -> Task<anyhow::Result<Option<Location>>> {
7090 let Some(project) = self.project.clone() else {
7091 return Task::Ready(Some(Ok(None)));
7092 };
7093
7094 cx.spawn(move |editor, mut cx| async move {
7095 let location_task = editor.update(&mut cx, |editor, cx| {
7096 project.update(cx, |project, cx| {
7097 let language_server_name =
7098 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7099 project
7100 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7101 .map(|(_, lsp_adapter)| {
7102 LanguageServerName(Arc::from(lsp_adapter.name()))
7103 })
7104 });
7105 language_server_name.map(|language_server_name| {
7106 project.open_local_buffer_via_lsp(
7107 lsp_location.uri.clone(),
7108 server_id,
7109 language_server_name,
7110 cx,
7111 )
7112 })
7113 })
7114 })?;
7115 let location = match location_task {
7116 Some(task) => Some({
7117 let target_buffer_handle = task.await.context("open local buffer")?;
7118 let range = {
7119 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7120 let target_start = target_buffer.clip_point_utf16(
7121 point_from_lsp(lsp_location.range.start),
7122 Bias::Left,
7123 );
7124 let target_end = target_buffer.clip_point_utf16(
7125 point_from_lsp(lsp_location.range.end),
7126 Bias::Left,
7127 );
7128 target_buffer.anchor_after(target_start)
7129 ..target_buffer.anchor_before(target_end)
7130 })
7131 };
7132 Location {
7133 buffer: target_buffer_handle,
7134 range,
7135 }
7136 }),
7137 None => None,
7138 };
7139 Ok(location)
7140 })
7141 }
7142
7143 pub fn find_all_references(
7144 workspace: &mut Workspace,
7145 _: &FindAllReferences,
7146 cx: &mut ViewContext<Workspace>,
7147 ) -> Option<Task<Result<()>>> {
7148 let active_item = workspace.active_item(cx)?;
7149 let editor_handle = active_item.act_as::<Self>(cx)?;
7150
7151 let editor = editor_handle.read(cx);
7152 let buffer = editor.buffer.read(cx);
7153 let head = editor.selections.newest::<usize>(cx).head();
7154 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7155 let replica_id = editor.replica_id(cx);
7156
7157 let project = workspace.project().clone();
7158 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7159 Some(cx.spawn_labeled(
7160 "Finding All References...",
7161 |workspace, mut cx| async move {
7162 let locations = references.await?;
7163 if locations.is_empty() {
7164 return Ok(());
7165 }
7166
7167 workspace.update(&mut cx, |workspace, cx| {
7168 let title = locations
7169 .first()
7170 .as_ref()
7171 .map(|location| {
7172 let buffer = location.buffer.read(cx);
7173 format!(
7174 "References to `{}`",
7175 buffer
7176 .text_for_range(location.range.clone())
7177 .collect::<String>()
7178 )
7179 })
7180 .unwrap();
7181 Self::open_locations_in_multibuffer(
7182 workspace, locations, replica_id, title, false, cx,
7183 );
7184 })?;
7185
7186 Ok(())
7187 },
7188 ))
7189 }
7190
7191 /// Opens a multibuffer with the given project locations in it
7192 pub fn open_locations_in_multibuffer(
7193 workspace: &mut Workspace,
7194 mut locations: Vec<Location>,
7195 replica_id: ReplicaId,
7196 title: String,
7197 split: bool,
7198 cx: &mut ViewContext<Workspace>,
7199 ) {
7200 // If there are multiple definitions, open them in a multibuffer
7201 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7202 let mut locations = locations.into_iter().peekable();
7203 let mut ranges_to_highlight = Vec::new();
7204
7205 let excerpt_buffer = cx.add_model(|cx| {
7206 let mut multibuffer = MultiBuffer::new(replica_id);
7207 while let Some(location) = locations.next() {
7208 let buffer = location.buffer.read(cx);
7209 let mut ranges_for_buffer = Vec::new();
7210 let range = location.range.to_offset(buffer);
7211 ranges_for_buffer.push(range.clone());
7212
7213 while let Some(next_location) = locations.peek() {
7214 if next_location.buffer == location.buffer {
7215 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7216 locations.next();
7217 } else {
7218 break;
7219 }
7220 }
7221
7222 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7223 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7224 location.buffer.clone(),
7225 ranges_for_buffer,
7226 1,
7227 cx,
7228 ))
7229 }
7230
7231 multibuffer.with_title(title)
7232 });
7233
7234 let editor = cx.add_view(|cx| {
7235 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7236 });
7237 editor.update(cx, |editor, cx| {
7238 editor.highlight_background::<Self>(
7239 ranges_to_highlight,
7240 |theme| theme.editor.highlighted_line_background,
7241 cx,
7242 );
7243 });
7244 if split {
7245 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7246 } else {
7247 workspace.add_item(Box::new(editor), cx);
7248 }
7249 }
7250
7251 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7252 use language::ToOffset as _;
7253
7254 let project = self.project.clone()?;
7255 let selection = self.selections.newest_anchor().clone();
7256 let (cursor_buffer, cursor_buffer_position) = self
7257 .buffer
7258 .read(cx)
7259 .text_anchor_for_position(selection.head(), cx)?;
7260 let (tail_buffer, _) = self
7261 .buffer
7262 .read(cx)
7263 .text_anchor_for_position(selection.tail(), cx)?;
7264 if tail_buffer != cursor_buffer {
7265 return None;
7266 }
7267
7268 let snapshot = cursor_buffer.read(cx).snapshot();
7269 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7270 let prepare_rename = project.update(cx, |project, cx| {
7271 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7272 });
7273
7274 Some(cx.spawn(|this, mut cx| async move {
7275 let rename_range = if let Some(range) = prepare_rename.await? {
7276 Some(range)
7277 } else {
7278 this.update(&mut cx, |this, cx| {
7279 let buffer = this.buffer.read(cx).snapshot(cx);
7280 let mut buffer_highlights = this
7281 .document_highlights_for_position(selection.head(), &buffer)
7282 .filter(|highlight| {
7283 highlight.start.excerpt_id() == selection.head().excerpt_id()
7284 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7285 });
7286 buffer_highlights
7287 .next()
7288 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7289 })?
7290 };
7291 if let Some(rename_range) = rename_range {
7292 let rename_buffer_range = rename_range.to_offset(&snapshot);
7293 let cursor_offset_in_rename_range =
7294 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7295
7296 this.update(&mut cx, |this, cx| {
7297 this.take_rename(false, cx);
7298 let style = this.style(cx);
7299 let buffer = this.buffer.read(cx).read(cx);
7300 let cursor_offset = selection.head().to_offset(&buffer);
7301 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7302 let rename_end = rename_start + rename_buffer_range.len();
7303 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7304 let mut old_highlight_id = None;
7305 let old_name: Arc<str> = buffer
7306 .chunks(rename_start..rename_end, true)
7307 .map(|chunk| {
7308 if old_highlight_id.is_none() {
7309 old_highlight_id = chunk.syntax_highlight_id;
7310 }
7311 chunk.text
7312 })
7313 .collect::<String>()
7314 .into();
7315
7316 drop(buffer);
7317
7318 // Position the selection in the rename editor so that it matches the current selection.
7319 this.show_local_selections = false;
7320 let rename_editor = cx.add_view(|cx| {
7321 let mut editor = Editor::single_line(None, cx);
7322 if let Some(old_highlight_id) = old_highlight_id {
7323 editor.override_text_style =
7324 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7325 }
7326 editor.buffer.update(cx, |buffer, cx| {
7327 buffer.edit([(0..0, old_name.clone())], None, cx)
7328 });
7329 editor.select_all(&SelectAll, cx);
7330 editor
7331 });
7332
7333 let ranges = this
7334 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7335 .into_iter()
7336 .flat_map(|(_, ranges)| ranges.into_iter())
7337 .chain(
7338 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7339 .into_iter()
7340 .flat_map(|(_, ranges)| ranges.into_iter()),
7341 )
7342 .collect();
7343
7344 this.highlight_text::<Rename>(
7345 ranges,
7346 HighlightStyle {
7347 fade_out: Some(style.rename_fade),
7348 ..Default::default()
7349 },
7350 cx,
7351 );
7352 cx.focus(&rename_editor);
7353 let block_id = this.insert_blocks(
7354 [BlockProperties {
7355 style: BlockStyle::Flex,
7356 position: range.start.clone(),
7357 height: 1,
7358 render: Arc::new({
7359 let editor = rename_editor.clone();
7360 move |cx: &mut BlockContext| {
7361 ChildView::new(&editor, cx)
7362 .contained()
7363 .with_padding_left(cx.anchor_x)
7364 .into_any()
7365 }
7366 }),
7367 disposition: BlockDisposition::Below,
7368 }],
7369 Some(Autoscroll::fit()),
7370 cx,
7371 )[0];
7372 this.pending_rename = Some(RenameState {
7373 range,
7374 old_name,
7375 editor: rename_editor,
7376 block_id,
7377 });
7378 })?;
7379 }
7380
7381 Ok(())
7382 }))
7383 }
7384
7385 pub fn confirm_rename(
7386 workspace: &mut Workspace,
7387 _: &ConfirmRename,
7388 cx: &mut ViewContext<Workspace>,
7389 ) -> Option<Task<Result<()>>> {
7390 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7391
7392 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7393 let rename = editor.take_rename(false, cx)?;
7394 let buffer = editor.buffer.read(cx);
7395 let (start_buffer, start) =
7396 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7397 let (end_buffer, end) =
7398 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7399 if start_buffer == end_buffer {
7400 let new_name = rename.editor.read(cx).text(cx);
7401 Some((start_buffer, start..end, rename.old_name, new_name))
7402 } else {
7403 None
7404 }
7405 })?;
7406
7407 let rename = workspace.project().clone().update(cx, |project, cx| {
7408 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7409 });
7410
7411 let editor = editor.downgrade();
7412 Some(cx.spawn(|workspace, mut cx| async move {
7413 let project_transaction = rename.await?;
7414 Self::open_project_transaction(
7415 &editor,
7416 workspace,
7417 project_transaction,
7418 format!("Rename: {} → {}", old_name, new_name),
7419 cx.clone(),
7420 )
7421 .await?;
7422
7423 editor.update(&mut cx, |editor, cx| {
7424 editor.refresh_document_highlights(cx);
7425 })?;
7426 Ok(())
7427 }))
7428 }
7429
7430 fn take_rename(
7431 &mut self,
7432 moving_cursor: bool,
7433 cx: &mut ViewContext<Self>,
7434 ) -> Option<RenameState> {
7435 let rename = self.pending_rename.take()?;
7436 self.remove_blocks(
7437 [rename.block_id].into_iter().collect(),
7438 Some(Autoscroll::fit()),
7439 cx,
7440 );
7441 self.clear_highlights::<Rename>(cx);
7442 self.show_local_selections = true;
7443
7444 if moving_cursor {
7445 let rename_editor = rename.editor.read(cx);
7446 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7447
7448 // Update the selection to match the position of the selection inside
7449 // the rename editor.
7450 let snapshot = self.buffer.read(cx).read(cx);
7451 let rename_range = rename.range.to_offset(&snapshot);
7452 let cursor_in_editor = snapshot
7453 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7454 .min(rename_range.end);
7455 drop(snapshot);
7456
7457 self.change_selections(None, cx, |s| {
7458 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7459 });
7460 } else {
7461 self.refresh_document_highlights(cx);
7462 }
7463
7464 Some(rename)
7465 }
7466
7467 #[cfg(any(test, feature = "test-support"))]
7468 pub fn pending_rename(&self) -> Option<&RenameState> {
7469 self.pending_rename.as_ref()
7470 }
7471
7472 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7473 let project = match &self.project {
7474 Some(project) => project.clone(),
7475 None => return None,
7476 };
7477
7478 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7479 }
7480
7481 fn perform_format(
7482 &mut self,
7483 project: ModelHandle<Project>,
7484 trigger: FormatTrigger,
7485 cx: &mut ViewContext<Self>,
7486 ) -> Task<Result<()>> {
7487 let buffer = self.buffer().clone();
7488 let buffers = buffer.read(cx).all_buffers();
7489
7490 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7491 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7492
7493 cx.spawn(|_, mut cx| async move {
7494 let transaction = futures::select_biased! {
7495 _ = timeout => {
7496 log::warn!("timed out waiting for formatting");
7497 None
7498 }
7499 transaction = format.log_err().fuse() => transaction,
7500 };
7501
7502 buffer.update(&mut cx, |buffer, cx| {
7503 if let Some(transaction) = transaction {
7504 if !buffer.is_singleton() {
7505 buffer.push_transaction(&transaction.0, cx);
7506 }
7507 }
7508
7509 cx.notify();
7510 });
7511
7512 Ok(())
7513 })
7514 }
7515
7516 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7517 if let Some(project) = self.project.clone() {
7518 self.buffer.update(cx, |multi_buffer, cx| {
7519 project.update(cx, |project, cx| {
7520 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7521 });
7522 })
7523 }
7524 }
7525
7526 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7527 cx.show_character_palette();
7528 }
7529
7530 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7531 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7532 let buffer = self.buffer.read(cx).snapshot(cx);
7533 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7534 let is_valid = buffer
7535 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7536 .any(|entry| {
7537 entry.diagnostic.is_primary
7538 && !entry.range.is_empty()
7539 && entry.range.start == primary_range_start
7540 && entry.diagnostic.message == active_diagnostics.primary_message
7541 });
7542
7543 if is_valid != active_diagnostics.is_valid {
7544 active_diagnostics.is_valid = is_valid;
7545 let mut new_styles = HashMap::default();
7546 for (block_id, diagnostic) in &active_diagnostics.blocks {
7547 new_styles.insert(
7548 *block_id,
7549 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7550 );
7551 }
7552 self.display_map
7553 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7554 }
7555 }
7556 }
7557
7558 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7559 self.dismiss_diagnostics(cx);
7560 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7561 let buffer = self.buffer.read(cx).snapshot(cx);
7562
7563 let mut primary_range = None;
7564 let mut primary_message = None;
7565 let mut group_end = Point::zero();
7566 let diagnostic_group = buffer
7567 .diagnostic_group::<Point>(group_id)
7568 .map(|entry| {
7569 if entry.range.end > group_end {
7570 group_end = entry.range.end;
7571 }
7572 if entry.diagnostic.is_primary {
7573 primary_range = Some(entry.range.clone());
7574 primary_message = Some(entry.diagnostic.message.clone());
7575 }
7576 entry
7577 })
7578 .collect::<Vec<_>>();
7579 let primary_range = primary_range?;
7580 let primary_message = primary_message?;
7581 let primary_range =
7582 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7583
7584 let blocks = display_map
7585 .insert_blocks(
7586 diagnostic_group.iter().map(|entry| {
7587 let diagnostic = entry.diagnostic.clone();
7588 let message_height = diagnostic.message.lines().count() as u8;
7589 BlockProperties {
7590 style: BlockStyle::Fixed,
7591 position: buffer.anchor_after(entry.range.start),
7592 height: message_height,
7593 render: diagnostic_block_renderer(diagnostic, true),
7594 disposition: BlockDisposition::Below,
7595 }
7596 }),
7597 cx,
7598 )
7599 .into_iter()
7600 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7601 .collect();
7602
7603 Some(ActiveDiagnosticGroup {
7604 primary_range,
7605 primary_message,
7606 blocks,
7607 is_valid: true,
7608 })
7609 });
7610 self.active_diagnostics.is_some()
7611 }
7612
7613 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7614 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7615 self.display_map.update(cx, |display_map, cx| {
7616 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7617 });
7618 cx.notify();
7619 }
7620 }
7621
7622 pub fn set_selections_from_remote(
7623 &mut self,
7624 selections: Vec<Selection<Anchor>>,
7625 pending_selection: Option<Selection<Anchor>>,
7626 cx: &mut ViewContext<Self>,
7627 ) {
7628 let old_cursor_position = self.selections.newest_anchor().head();
7629 self.selections.change_with(cx, |s| {
7630 s.select_anchors(selections);
7631 if let Some(pending_selection) = pending_selection {
7632 s.set_pending(pending_selection, SelectMode::Character);
7633 } else {
7634 s.clear_pending();
7635 }
7636 });
7637 self.selections_did_change(false, &old_cursor_position, cx);
7638 }
7639
7640 fn push_to_selection_history(&mut self) {
7641 self.selection_history.push(SelectionHistoryEntry {
7642 selections: self.selections.disjoint_anchors(),
7643 select_next_state: self.select_next_state.clone(),
7644 select_prev_state: self.select_prev_state.clone(),
7645 add_selections_state: self.add_selections_state.clone(),
7646 });
7647 }
7648
7649 pub fn transact(
7650 &mut self,
7651 cx: &mut ViewContext<Self>,
7652 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7653 ) -> Option<TransactionId> {
7654 self.start_transaction_at(Instant::now(), cx);
7655 update(self, cx);
7656 self.end_transaction_at(Instant::now(), cx)
7657 }
7658
7659 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7660 self.end_selection(cx);
7661 if let Some(tx_id) = self
7662 .buffer
7663 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7664 {
7665 self.selection_history
7666 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7667 }
7668 }
7669
7670 fn end_transaction_at(
7671 &mut self,
7672 now: Instant,
7673 cx: &mut ViewContext<Self>,
7674 ) -> Option<TransactionId> {
7675 if let Some(tx_id) = self
7676 .buffer
7677 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7678 {
7679 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7680 *end_selections = Some(self.selections.disjoint_anchors());
7681 } else {
7682 error!("unexpectedly ended a transaction that wasn't started by this editor");
7683 }
7684
7685 cx.emit(Event::Edited);
7686 Some(tx_id)
7687 } else {
7688 None
7689 }
7690 }
7691
7692 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7693 let mut fold_ranges = Vec::new();
7694
7695 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7696
7697 let selections = self.selections.all_adjusted(cx);
7698 for selection in selections {
7699 let range = selection.range().sorted();
7700 let buffer_start_row = range.start.row;
7701
7702 for row in (0..=range.end.row).rev() {
7703 let fold_range = display_map.foldable_range(row);
7704
7705 if let Some(fold_range) = fold_range {
7706 if fold_range.end.row >= buffer_start_row {
7707 fold_ranges.push(fold_range);
7708 if row <= range.start.row {
7709 break;
7710 }
7711 }
7712 }
7713 }
7714 }
7715
7716 self.fold_ranges(fold_ranges, true, cx);
7717 }
7718
7719 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7720 let buffer_row = fold_at.buffer_row;
7721 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7722
7723 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7724 let autoscroll = self
7725 .selections
7726 .all::<Point>(cx)
7727 .iter()
7728 .any(|selection| fold_range.overlaps(&selection.range()));
7729
7730 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7731 }
7732 }
7733
7734 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7735 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7736 let buffer = &display_map.buffer_snapshot;
7737 let selections = self.selections.all::<Point>(cx);
7738 let ranges = selections
7739 .iter()
7740 .map(|s| {
7741 let range = s.display_range(&display_map).sorted();
7742 let mut start = range.start.to_point(&display_map);
7743 let mut end = range.end.to_point(&display_map);
7744 start.column = 0;
7745 end.column = buffer.line_len(end.row);
7746 start..end
7747 })
7748 .collect::<Vec<_>>();
7749
7750 self.unfold_ranges(ranges, true, true, cx);
7751 }
7752
7753 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7754 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7755
7756 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7757 ..Point::new(
7758 unfold_at.buffer_row,
7759 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7760 );
7761
7762 let autoscroll = self
7763 .selections
7764 .all::<Point>(cx)
7765 .iter()
7766 .any(|selection| selection.range().overlaps(&intersection_range));
7767
7768 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7769 }
7770
7771 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7772 let selections = self.selections.all::<Point>(cx);
7773 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7774 let line_mode = self.selections.line_mode;
7775 let ranges = selections.into_iter().map(|s| {
7776 if line_mode {
7777 let start = Point::new(s.start.row, 0);
7778 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
7779 start..end
7780 } else {
7781 s.start..s.end
7782 }
7783 });
7784 self.fold_ranges(ranges, true, cx);
7785 }
7786
7787 pub fn fold_ranges<T: ToOffset + Clone>(
7788 &mut self,
7789 ranges: impl IntoIterator<Item = Range<T>>,
7790 auto_scroll: bool,
7791 cx: &mut ViewContext<Self>,
7792 ) {
7793 let mut ranges = ranges.into_iter().peekable();
7794 if ranges.peek().is_some() {
7795 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7796
7797 if auto_scroll {
7798 self.request_autoscroll(Autoscroll::fit(), cx);
7799 }
7800
7801 cx.notify();
7802 }
7803 }
7804
7805 pub fn unfold_ranges<T: ToOffset + Clone>(
7806 &mut self,
7807 ranges: impl IntoIterator<Item = Range<T>>,
7808 inclusive: bool,
7809 auto_scroll: bool,
7810 cx: &mut ViewContext<Self>,
7811 ) {
7812 let mut ranges = ranges.into_iter().peekable();
7813 if ranges.peek().is_some() {
7814 self.display_map
7815 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7816 if auto_scroll {
7817 self.request_autoscroll(Autoscroll::fit(), cx);
7818 }
7819
7820 cx.notify();
7821 }
7822 }
7823
7824 pub fn gutter_hover(
7825 &mut self,
7826 GutterHover { hovered }: &GutterHover,
7827 cx: &mut ViewContext<Self>,
7828 ) {
7829 self.gutter_hovered = *hovered;
7830 cx.notify();
7831 }
7832
7833 pub fn insert_blocks(
7834 &mut self,
7835 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7836 autoscroll: Option<Autoscroll>,
7837 cx: &mut ViewContext<Self>,
7838 ) -> Vec<BlockId> {
7839 let blocks = self
7840 .display_map
7841 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7842 if let Some(autoscroll) = autoscroll {
7843 self.request_autoscroll(autoscroll, cx);
7844 }
7845 blocks
7846 }
7847
7848 pub fn replace_blocks(
7849 &mut self,
7850 blocks: HashMap<BlockId, RenderBlock>,
7851 autoscroll: Option<Autoscroll>,
7852 cx: &mut ViewContext<Self>,
7853 ) {
7854 self.display_map
7855 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7856 if let Some(autoscroll) = autoscroll {
7857 self.request_autoscroll(autoscroll, cx);
7858 }
7859 }
7860
7861 pub fn remove_blocks(
7862 &mut self,
7863 block_ids: HashSet<BlockId>,
7864 autoscroll: Option<Autoscroll>,
7865 cx: &mut ViewContext<Self>,
7866 ) {
7867 self.display_map.update(cx, |display_map, cx| {
7868 display_map.remove_blocks(block_ids, cx)
7869 });
7870 if let Some(autoscroll) = autoscroll {
7871 self.request_autoscroll(autoscroll, cx);
7872 }
7873 }
7874
7875 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7876 self.display_map
7877 .update(cx, |map, cx| map.snapshot(cx))
7878 .longest_row()
7879 }
7880
7881 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7882 self.display_map
7883 .update(cx, |map, cx| map.snapshot(cx))
7884 .max_point()
7885 }
7886
7887 pub fn text(&self, cx: &AppContext) -> String {
7888 self.buffer.read(cx).read(cx).text()
7889 }
7890
7891 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7892 self.transact(cx, |this, cx| {
7893 this.buffer
7894 .read(cx)
7895 .as_singleton()
7896 .expect("you can only call set_text on editors for singleton buffers")
7897 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7898 });
7899 }
7900
7901 pub fn display_text(&self, cx: &mut AppContext) -> String {
7902 self.display_map
7903 .update(cx, |map, cx| map.snapshot(cx))
7904 .text()
7905 }
7906
7907 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7908 let mut wrap_guides = smallvec::smallvec![];
7909
7910 if self.show_wrap_guides == Some(false) {
7911 return wrap_guides;
7912 }
7913
7914 let settings = self.buffer.read(cx).settings_at(0, cx);
7915 if settings.show_wrap_guides {
7916 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7917 wrap_guides.push((soft_wrap as usize, true));
7918 }
7919 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7920 }
7921
7922 wrap_guides
7923 }
7924
7925 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7926 let settings = self.buffer.read(cx).settings_at(0, cx);
7927 let mode = self
7928 .soft_wrap_mode_override
7929 .unwrap_or_else(|| settings.soft_wrap);
7930 match mode {
7931 language_settings::SoftWrap::None => SoftWrap::None,
7932 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7933 language_settings::SoftWrap::PreferredLineLength => {
7934 SoftWrap::Column(settings.preferred_line_length)
7935 }
7936 }
7937 }
7938
7939 pub fn set_soft_wrap_mode(
7940 &mut self,
7941 mode: language_settings::SoftWrap,
7942 cx: &mut ViewContext<Self>,
7943 ) {
7944 self.soft_wrap_mode_override = Some(mode);
7945 cx.notify();
7946 }
7947
7948 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7949 self.display_map
7950 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7951 }
7952
7953 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7954 if self.soft_wrap_mode_override.is_some() {
7955 self.soft_wrap_mode_override.take();
7956 } else {
7957 let soft_wrap = match self.soft_wrap_mode(cx) {
7958 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7959 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7960 };
7961 self.soft_wrap_mode_override = Some(soft_wrap);
7962 }
7963 cx.notify();
7964 }
7965
7966 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7967 self.show_gutter = show_gutter;
7968 cx.notify();
7969 }
7970
7971 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7972 self.show_wrap_guides = Some(show_gutter);
7973 cx.notify();
7974 }
7975
7976 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7977 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7978 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7979 cx.reveal_path(&file.abs_path(cx));
7980 }
7981 }
7982 }
7983
7984 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7985 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7986 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7987 if let Some(path) = file.abs_path(cx).to_str() {
7988 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7989 }
7990 }
7991 }
7992 }
7993
7994 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7995 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7996 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7997 if let Some(path) = file.path().to_str() {
7998 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7999 }
8000 }
8001 }
8002 }
8003
8004 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
8005 self.highlighted_rows = rows;
8006 }
8007
8008 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
8009 self.highlighted_rows.clone()
8010 }
8011
8012 pub fn highlight_background<T: 'static>(
8013 &mut self,
8014 ranges: Vec<Range<Anchor>>,
8015 color_fetcher: fn(&Theme) -> Color,
8016 cx: &mut ViewContext<Self>,
8017 ) {
8018 self.background_highlights
8019 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
8020 cx.notify();
8021 }
8022
8023 pub fn highlight_inlay_background<T: 'static>(
8024 &mut self,
8025 ranges: Vec<InlayHighlight>,
8026 color_fetcher: fn(&Theme) -> Color,
8027 cx: &mut ViewContext<Self>,
8028 ) {
8029 // TODO: no actual highlights happen for inlays currently, find a way to do that
8030 self.inlay_background_highlights
8031 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
8032 cx.notify();
8033 }
8034
8035 pub fn clear_background_highlights<T: 'static>(
8036 &mut self,
8037 cx: &mut ViewContext<Self>,
8038 ) -> Option<BackgroundHighlight> {
8039 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
8040 let inlay_highlights = self
8041 .inlay_background_highlights
8042 .remove(&Some(TypeId::of::<T>()));
8043 if text_highlights.is_some() || inlay_highlights.is_some() {
8044 cx.notify();
8045 }
8046 text_highlights
8047 }
8048
8049 #[cfg(feature = "test-support")]
8050 pub fn all_text_background_highlights(
8051 &mut self,
8052 cx: &mut ViewContext<Self>,
8053 ) -> Vec<(Range<DisplayPoint>, Color)> {
8054 let snapshot = self.snapshot(cx);
8055 let buffer = &snapshot.buffer_snapshot;
8056 let start = buffer.anchor_before(0);
8057 let end = buffer.anchor_after(buffer.len());
8058 let theme = theme::current(cx);
8059 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
8060 }
8061
8062 fn document_highlights_for_position<'a>(
8063 &'a self,
8064 position: Anchor,
8065 buffer: &'a MultiBufferSnapshot,
8066 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
8067 let read_highlights = self
8068 .background_highlights
8069 .get(&TypeId::of::<DocumentHighlightRead>())
8070 .map(|h| &h.1);
8071 let write_highlights = self
8072 .background_highlights
8073 .get(&TypeId::of::<DocumentHighlightWrite>())
8074 .map(|h| &h.1);
8075 let left_position = position.bias_left(buffer);
8076 let right_position = position.bias_right(buffer);
8077 read_highlights
8078 .into_iter()
8079 .chain(write_highlights)
8080 .flat_map(move |ranges| {
8081 let start_ix = match ranges.binary_search_by(|probe| {
8082 let cmp = probe.end.cmp(&left_position, buffer);
8083 if cmp.is_ge() {
8084 Ordering::Greater
8085 } else {
8086 Ordering::Less
8087 }
8088 }) {
8089 Ok(i) | Err(i) => i,
8090 };
8091
8092 let right_position = right_position.clone();
8093 ranges[start_ix..]
8094 .iter()
8095 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8096 })
8097 }
8098
8099 pub fn background_highlights_in_range(
8100 &self,
8101 search_range: Range<Anchor>,
8102 display_snapshot: &DisplaySnapshot,
8103 theme: &Theme,
8104 ) -> Vec<(Range<DisplayPoint>, Color)> {
8105 let mut results = Vec::new();
8106 for (color_fetcher, ranges) in self.background_highlights.values() {
8107 let color = color_fetcher(theme);
8108 let start_ix = match ranges.binary_search_by(|probe| {
8109 let cmp = probe
8110 .end
8111 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8112 if cmp.is_gt() {
8113 Ordering::Greater
8114 } else {
8115 Ordering::Less
8116 }
8117 }) {
8118 Ok(i) | Err(i) => i,
8119 };
8120 for range in &ranges[start_ix..] {
8121 if range
8122 .start
8123 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8124 .is_ge()
8125 {
8126 break;
8127 }
8128
8129 let start = range.start.to_display_point(&display_snapshot);
8130 let end = range.end.to_display_point(&display_snapshot);
8131 results.push((start..end, color))
8132 }
8133 }
8134 results
8135 }
8136
8137 pub fn background_highlight_row_ranges<T: 'static>(
8138 &self,
8139 search_range: Range<Anchor>,
8140 display_snapshot: &DisplaySnapshot,
8141 count: usize,
8142 ) -> Vec<RangeInclusive<DisplayPoint>> {
8143 let mut results = Vec::new();
8144 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8145 return vec![];
8146 };
8147
8148 let start_ix = match ranges.binary_search_by(|probe| {
8149 let cmp = probe
8150 .end
8151 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8152 if cmp.is_gt() {
8153 Ordering::Greater
8154 } else {
8155 Ordering::Less
8156 }
8157 }) {
8158 Ok(i) | Err(i) => i,
8159 };
8160 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8161 if let (Some(start_display), Some(end_display)) = (start, end) {
8162 results.push(
8163 start_display.to_display_point(display_snapshot)
8164 ..=end_display.to_display_point(display_snapshot),
8165 );
8166 }
8167 };
8168 let mut start_row: Option<Point> = None;
8169 let mut end_row: Option<Point> = None;
8170 if ranges.len() > count {
8171 return Vec::new();
8172 }
8173 for range in &ranges[start_ix..] {
8174 if range
8175 .start
8176 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8177 .is_ge()
8178 {
8179 break;
8180 }
8181 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8182 if let Some(current_row) = &end_row {
8183 if end.row == current_row.row {
8184 continue;
8185 }
8186 }
8187 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8188 if start_row.is_none() {
8189 assert_eq!(end_row, None);
8190 start_row = Some(start);
8191 end_row = Some(end);
8192 continue;
8193 }
8194 if let Some(current_end) = end_row.as_mut() {
8195 if start.row > current_end.row + 1 {
8196 push_region(start_row, end_row);
8197 start_row = Some(start);
8198 end_row = Some(end);
8199 } else {
8200 // Merge two hunks.
8201 *current_end = end;
8202 }
8203 } else {
8204 unreachable!();
8205 }
8206 }
8207 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8208 push_region(start_row, end_row);
8209 results
8210 }
8211
8212 pub fn highlight_text<T: 'static>(
8213 &mut self,
8214 ranges: Vec<Range<Anchor>>,
8215 style: HighlightStyle,
8216 cx: &mut ViewContext<Self>,
8217 ) {
8218 self.display_map.update(cx, |map, _| {
8219 map.highlight_text(TypeId::of::<T>(), ranges, style)
8220 });
8221 cx.notify();
8222 }
8223
8224 pub fn highlight_inlays<T: 'static>(
8225 &mut self,
8226 highlights: Vec<InlayHighlight>,
8227 style: HighlightStyle,
8228 cx: &mut ViewContext<Self>,
8229 ) {
8230 self.display_map.update(cx, |map, _| {
8231 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8232 });
8233 cx.notify();
8234 }
8235
8236 pub fn text_highlights<'a, T: 'static>(
8237 &'a self,
8238 cx: &'a AppContext,
8239 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8240 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8241 }
8242
8243 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8244 let cleared = self
8245 .display_map
8246 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8247 if cleared {
8248 cx.notify();
8249 }
8250 }
8251
8252 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8253 self.blink_manager.read(cx).visible() && self.focused
8254 }
8255
8256 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8257 cx.notify();
8258 }
8259
8260 fn on_buffer_event(
8261 &mut self,
8262 multibuffer: ModelHandle<MultiBuffer>,
8263 event: &multi_buffer::Event,
8264 cx: &mut ViewContext<Self>,
8265 ) {
8266 match event {
8267 multi_buffer::Event::Edited {
8268 sigleton_buffer_edited,
8269 } => {
8270 self.refresh_active_diagnostics(cx);
8271 self.refresh_code_actions(cx);
8272 if self.has_active_copilot_suggestion(cx) {
8273 self.update_visible_copilot_suggestion(cx);
8274 }
8275 cx.emit(Event::BufferEdited);
8276
8277 if *sigleton_buffer_edited {
8278 if let Some(project) = &self.project {
8279 let project = project.read(cx);
8280 let languages_affected = multibuffer
8281 .read(cx)
8282 .all_buffers()
8283 .into_iter()
8284 .filter_map(|buffer| {
8285 let buffer = buffer.read(cx);
8286 let language = buffer.language()?;
8287 if project.is_local()
8288 && project.language_servers_for_buffer(buffer, cx).count() == 0
8289 {
8290 None
8291 } else {
8292 Some(language)
8293 }
8294 })
8295 .cloned()
8296 .collect::<HashSet<_>>();
8297 if !languages_affected.is_empty() {
8298 self.refresh_inlay_hints(
8299 InlayHintRefreshReason::BufferEdited(languages_affected),
8300 cx,
8301 );
8302 }
8303 }
8304 }
8305 }
8306 multi_buffer::Event::ExcerptsAdded {
8307 buffer,
8308 predecessor,
8309 excerpts,
8310 } => {
8311 cx.emit(Event::ExcerptsAdded {
8312 buffer: buffer.clone(),
8313 predecessor: *predecessor,
8314 excerpts: excerpts.clone(),
8315 });
8316 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8317 }
8318 multi_buffer::Event::ExcerptsRemoved { ids } => {
8319 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8320 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8321 }
8322 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8323 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8324 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8325 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8326 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8327 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8328 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8329 multi_buffer::Event::DiagnosticsUpdated => {
8330 self.refresh_active_diagnostics(cx);
8331 }
8332 _ => {}
8333 };
8334 }
8335
8336 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8337 cx.notify();
8338 }
8339
8340 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8341 self.refresh_copilot_suggestions(true, cx);
8342 self.refresh_inlay_hints(
8343 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8344 self.selections.newest_anchor().head(),
8345 &self.buffer.read(cx).snapshot(cx),
8346 cx,
8347 )),
8348 cx,
8349 );
8350 }
8351
8352 pub fn set_searchable(&mut self, searchable: bool) {
8353 self.searchable = searchable;
8354 }
8355
8356 pub fn searchable(&self) -> bool {
8357 self.searchable
8358 }
8359
8360 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8361 let active_item = workspace.active_item(cx);
8362 let editor_handle = if let Some(editor) = active_item
8363 .as_ref()
8364 .and_then(|item| item.act_as::<Self>(cx))
8365 {
8366 editor
8367 } else {
8368 cx.propagate_action();
8369 return;
8370 };
8371
8372 let editor = editor_handle.read(cx);
8373 let buffer = editor.buffer.read(cx);
8374 if buffer.is_singleton() {
8375 cx.propagate_action();
8376 return;
8377 }
8378
8379 let mut new_selections_by_buffer = HashMap::default();
8380 for selection in editor.selections.all::<usize>(cx) {
8381 for (buffer, mut range, _) in
8382 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8383 {
8384 if selection.reversed {
8385 mem::swap(&mut range.start, &mut range.end);
8386 }
8387 new_selections_by_buffer
8388 .entry(buffer)
8389 .or_insert(Vec::new())
8390 .push(range)
8391 }
8392 }
8393
8394 editor_handle.update(cx, |editor, cx| {
8395 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8396 });
8397 let pane = workspace.active_pane().clone();
8398 pane.update(cx, |pane, _| pane.disable_history());
8399
8400 // We defer the pane interaction because we ourselves are a workspace item
8401 // and activating a new item causes the pane to call a method on us reentrantly,
8402 // which panics if we're on the stack.
8403 cx.defer(move |workspace, cx| {
8404 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8405 let editor = workspace.open_project_item::<Self>(buffer, cx);
8406 editor.update(cx, |editor, cx| {
8407 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8408 s.select_ranges(ranges);
8409 });
8410 });
8411 }
8412
8413 pane.update(cx, |pane, _| pane.enable_history());
8414 });
8415 }
8416
8417 fn jump(
8418 workspace: &mut Workspace,
8419 path: ProjectPath,
8420 position: Point,
8421 anchor: language::Anchor,
8422 cx: &mut ViewContext<Workspace>,
8423 ) {
8424 let editor = workspace.open_path(path, None, true, cx);
8425 cx.spawn(|_, mut cx| async move {
8426 let editor = editor
8427 .await?
8428 .downcast::<Editor>()
8429 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8430 .downgrade();
8431 editor.update(&mut cx, |editor, cx| {
8432 let buffer = editor
8433 .buffer()
8434 .read(cx)
8435 .as_singleton()
8436 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8437 let buffer = buffer.read(cx);
8438 let cursor = if buffer.can_resolve(&anchor) {
8439 language::ToPoint::to_point(&anchor, buffer)
8440 } else {
8441 buffer.clip_point(position, Bias::Left)
8442 };
8443
8444 let nav_history = editor.nav_history.take();
8445 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8446 s.select_ranges([cursor..cursor]);
8447 });
8448 editor.nav_history = nav_history;
8449
8450 anyhow::Ok(())
8451 })??;
8452
8453 anyhow::Ok(())
8454 })
8455 .detach_and_log_err(cx);
8456 }
8457
8458 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8459 let snapshot = self.buffer.read(cx).read(cx);
8460 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8461 Some(
8462 ranges
8463 .iter()
8464 .map(move |range| {
8465 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8466 })
8467 .collect(),
8468 )
8469 }
8470
8471 fn selection_replacement_ranges(
8472 &self,
8473 range: Range<OffsetUtf16>,
8474 cx: &AppContext,
8475 ) -> Vec<Range<OffsetUtf16>> {
8476 let selections = self.selections.all::<OffsetUtf16>(cx);
8477 let newest_selection = selections
8478 .iter()
8479 .max_by_key(|selection| selection.id)
8480 .unwrap();
8481 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8482 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8483 let snapshot = self.buffer.read(cx).read(cx);
8484 selections
8485 .into_iter()
8486 .map(|mut selection| {
8487 selection.start.0 =
8488 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8489 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8490 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8491 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8492 })
8493 .collect()
8494 }
8495
8496 fn report_copilot_event(
8497 &self,
8498 suggestion_id: Option<String>,
8499 suggestion_accepted: bool,
8500 cx: &AppContext,
8501 ) {
8502 let Some(project) = &self.project else { return };
8503
8504 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8505 let file_extension = self
8506 .buffer
8507 .read(cx)
8508 .as_singleton()
8509 .and_then(|b| b.read(cx).file())
8510 .and_then(|file| Path::new(file.file_name(cx)).extension())
8511 .and_then(|e| e.to_str())
8512 .map(|a| a.to_string());
8513
8514 let telemetry = project.read(cx).client().telemetry().clone();
8515 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8516
8517 let event = ClickhouseEvent::Copilot {
8518 suggestion_id,
8519 suggestion_accepted,
8520 file_extension,
8521 };
8522 telemetry.report_clickhouse_event(event, telemetry_settings);
8523 }
8524
8525 fn report_editor_event(
8526 &self,
8527 operation: &'static str,
8528 file_extension: Option<String>,
8529 cx: &AppContext,
8530 ) {
8531 let Some(project) = &self.project else { return };
8532
8533 // If None, we are in a file without an extension
8534 let file = self
8535 .buffer
8536 .read(cx)
8537 .as_singleton()
8538 .and_then(|b| b.read(cx).file());
8539 let file_extension = file_extension.or(file
8540 .as_ref()
8541 .and_then(|file| Path::new(file.file_name(cx)).extension())
8542 .and_then(|e| e.to_str())
8543 .map(|a| a.to_string()));
8544
8545 let vim_mode = cx
8546 .global::<SettingsStore>()
8547 .raw_user_settings()
8548 .get("vim_mode")
8549 == Some(&serde_json::Value::Bool(true));
8550 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8551 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8552 let copilot_enabled_for_language = self
8553 .buffer
8554 .read(cx)
8555 .settings_at(0, cx)
8556 .show_copilot_suggestions;
8557
8558 let telemetry = project.read(cx).client().telemetry().clone();
8559 let event = ClickhouseEvent::Editor {
8560 file_extension,
8561 vim_mode,
8562 operation,
8563 copilot_enabled,
8564 copilot_enabled_for_language,
8565 };
8566 telemetry.report_clickhouse_event(event, telemetry_settings)
8567 }
8568
8569 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8570 /// with each line being an array of {text, highlight} objects.
8571 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8572 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8573 return;
8574 };
8575
8576 #[derive(Serialize)]
8577 struct Chunk<'a> {
8578 text: String,
8579 highlight: Option<&'a str>,
8580 }
8581
8582 let snapshot = buffer.read(cx).snapshot();
8583 let range = self
8584 .selected_text_range(cx)
8585 .and_then(|selected_range| {
8586 if selected_range.is_empty() {
8587 None
8588 } else {
8589 Some(selected_range)
8590 }
8591 })
8592 .unwrap_or_else(|| 0..snapshot.len());
8593
8594 let chunks = snapshot.chunks(range, true);
8595 let mut lines = Vec::new();
8596 let mut line: VecDeque<Chunk> = VecDeque::new();
8597
8598 let theme = &theme::current(cx).editor.syntax;
8599
8600 for chunk in chunks {
8601 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8602 let mut chunk_lines = chunk.text.split("\n").peekable();
8603 while let Some(text) = chunk_lines.next() {
8604 let mut merged_with_last_token = false;
8605 if let Some(last_token) = line.back_mut() {
8606 if last_token.highlight == highlight {
8607 last_token.text.push_str(text);
8608 merged_with_last_token = true;
8609 }
8610 }
8611
8612 if !merged_with_last_token {
8613 line.push_back(Chunk {
8614 text: text.into(),
8615 highlight,
8616 });
8617 }
8618
8619 if chunk_lines.peek().is_some() {
8620 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8621 line.pop_front();
8622 }
8623 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8624 line.pop_back();
8625 }
8626
8627 lines.push(mem::take(&mut line));
8628 }
8629 }
8630 }
8631
8632 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8633 return;
8634 };
8635 cx.write_to_clipboard(ClipboardItem::new(lines));
8636 }
8637
8638 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8639 &self.inlay_hint_cache
8640 }
8641
8642 pub fn replay_insert_event(
8643 &mut self,
8644 text: &str,
8645 relative_utf16_range: Option<Range<isize>>,
8646 cx: &mut ViewContext<Self>,
8647 ) {
8648 if !self.input_enabled {
8649 cx.emit(Event::InputIgnored { text: text.into() });
8650 return;
8651 }
8652 if let Some(relative_utf16_range) = relative_utf16_range {
8653 let selections = self.selections.all::<OffsetUtf16>(cx);
8654 self.change_selections(None, cx, |s| {
8655 let new_ranges = selections.into_iter().map(|range| {
8656 let start = OffsetUtf16(
8657 range
8658 .head()
8659 .0
8660 .saturating_add_signed(relative_utf16_range.start),
8661 );
8662 let end = OffsetUtf16(
8663 range
8664 .head()
8665 .0
8666 .saturating_add_signed(relative_utf16_range.end),
8667 );
8668 start..end
8669 });
8670 s.select_ranges(new_ranges);
8671 });
8672 }
8673
8674 self.handle_input(text, cx);
8675 }
8676
8677 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
8678 let Some(project) = self.project.as_ref() else {
8679 return false;
8680 };
8681 let project = project.read(cx);
8682
8683 let mut supports = false;
8684 self.buffer().read(cx).for_each_buffer(|buffer| {
8685 if !supports {
8686 supports = project
8687 .language_servers_for_buffer(buffer.read(cx), cx)
8688 .any(
8689 |(_, server)| match server.capabilities().inlay_hint_provider {
8690 Some(lsp::OneOf::Left(enabled)) => enabled,
8691 Some(lsp::OneOf::Right(_)) => true,
8692 None => false,
8693 },
8694 )
8695 }
8696 });
8697 supports
8698 }
8699}
8700
8701pub trait CollaborationHub {
8702 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
8703 fn user_participant_indices<'a>(
8704 &self,
8705 cx: &'a AppContext,
8706 ) -> &'a HashMap<u64, ParticipantIndex>;
8707}
8708
8709impl CollaborationHub for ModelHandle<Project> {
8710 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
8711 self.read(cx).collaborators()
8712 }
8713
8714 fn user_participant_indices<'a>(
8715 &self,
8716 cx: &'a AppContext,
8717 ) -> &'a HashMap<u64, ParticipantIndex> {
8718 self.read(cx).user_store().read(cx).participant_indices()
8719 }
8720}
8721
8722fn inlay_hint_settings(
8723 location: Anchor,
8724 snapshot: &MultiBufferSnapshot,
8725 cx: &mut ViewContext<'_, '_, Editor>,
8726) -> InlayHintSettings {
8727 let file = snapshot.file_at(location);
8728 let language = snapshot.language_at(location);
8729 let settings = all_language_settings(file, cx);
8730 settings
8731 .language(language.map(|l| l.name()).as_deref())
8732 .inlay_hints
8733}
8734
8735fn consume_contiguous_rows(
8736 contiguous_row_selections: &mut Vec<Selection<Point>>,
8737 selection: &Selection<Point>,
8738 display_map: &DisplaySnapshot,
8739 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8740) -> (u32, u32) {
8741 contiguous_row_selections.push(selection.clone());
8742 let start_row = selection.start.row;
8743 let mut end_row = ending_row(selection, display_map);
8744
8745 while let Some(next_selection) = selections.peek() {
8746 if next_selection.start.row <= end_row {
8747 end_row = ending_row(next_selection, display_map);
8748 contiguous_row_selections.push(selections.next().unwrap().clone());
8749 } else {
8750 break;
8751 }
8752 }
8753 (start_row, end_row)
8754}
8755
8756fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8757 if next_selection.end.column > 0 || next_selection.is_empty() {
8758 display_map.next_line_boundary(next_selection.end).0.row + 1
8759 } else {
8760 next_selection.end.row
8761 }
8762}
8763
8764impl EditorSnapshot {
8765 pub fn remote_selections_in_range<'a>(
8766 &'a self,
8767 range: &'a Range<Anchor>,
8768 collaboration_hub: &dyn CollaborationHub,
8769 cx: &'a AppContext,
8770 ) -> impl 'a + Iterator<Item = RemoteSelection> {
8771 let participant_indices = collaboration_hub.user_participant_indices(cx);
8772 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
8773 let collaborators_by_replica_id = collaborators_by_peer_id
8774 .iter()
8775 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
8776 .collect::<HashMap<_, _>>();
8777 self.buffer_snapshot
8778 .remote_selections_in_range(range)
8779 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
8780 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
8781 let participant_index = participant_indices.get(&collaborator.user_id).copied();
8782 Some(RemoteSelection {
8783 replica_id,
8784 selection,
8785 cursor_shape,
8786 line_mode,
8787 participant_index,
8788 peer_id: collaborator.peer_id,
8789 })
8790 })
8791 }
8792
8793 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8794 self.display_snapshot.buffer_snapshot.language_at(position)
8795 }
8796
8797 pub fn is_focused(&self) -> bool {
8798 self.is_focused
8799 }
8800
8801 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8802 self.placeholder_text.as_ref()
8803 }
8804
8805 pub fn scroll_position(&self) -> Vector2F {
8806 self.scroll_anchor.scroll_position(&self.display_snapshot)
8807 }
8808}
8809
8810impl Deref for EditorSnapshot {
8811 type Target = DisplaySnapshot;
8812
8813 fn deref(&self) -> &Self::Target {
8814 &self.display_snapshot
8815 }
8816}
8817
8818#[derive(Clone, Debug, PartialEq, Eq)]
8819pub enum Event {
8820 InputIgnored {
8821 text: Arc<str>,
8822 },
8823 InputHandled {
8824 utf16_range_to_replace: Option<Range<isize>>,
8825 text: Arc<str>,
8826 },
8827 ExcerptsAdded {
8828 buffer: ModelHandle<Buffer>,
8829 predecessor: ExcerptId,
8830 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8831 },
8832 ExcerptsRemoved {
8833 ids: Vec<ExcerptId>,
8834 },
8835 BufferEdited,
8836 Edited,
8837 Reparsed,
8838 Focused,
8839 Blurred,
8840 DirtyChanged,
8841 Saved,
8842 TitleChanged,
8843 DiffBaseChanged,
8844 SelectionsChanged {
8845 local: bool,
8846 },
8847 ScrollPositionChanged {
8848 local: bool,
8849 autoscroll: bool,
8850 },
8851 Closed,
8852}
8853
8854pub struct EditorFocused(pub ViewHandle<Editor>);
8855pub struct EditorBlurred(pub ViewHandle<Editor>);
8856pub struct EditorReleased(pub WeakViewHandle<Editor>);
8857
8858impl Entity for Editor {
8859 type Event = Event;
8860
8861 fn release(&mut self, cx: &mut AppContext) {
8862 cx.emit_global(EditorReleased(self.handle.clone()));
8863 }
8864}
8865
8866impl View for Editor {
8867 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8868 let style = self.style(cx);
8869 let font_changed = self.display_map.update(cx, |map, cx| {
8870 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8871 map.set_font(style.text.font_id, style.text.font_size, cx)
8872 });
8873
8874 if font_changed {
8875 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8876 hide_hover(editor, cx);
8877 hide_link_definition(editor, cx);
8878 });
8879 }
8880
8881 Stack::new()
8882 .with_child(EditorElement::new(style.clone()))
8883 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8884 .into_any()
8885 }
8886
8887 fn ui_name() -> &'static str {
8888 "Editor"
8889 }
8890
8891 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
8892 if cx.is_self_focused() {
8893 let focused_event = EditorFocused(cx.handle());
8894 cx.emit(Event::Focused);
8895 cx.emit_global(focused_event);
8896 }
8897 if let Some(rename) = self.pending_rename.as_ref() {
8898 cx.focus(&rename.editor);
8899 } else if cx.is_self_focused() || !focused.is::<Editor>() {
8900 if !self.focused {
8901 self.blink_manager.update(cx, BlinkManager::enable);
8902 }
8903 self.focused = true;
8904 self.buffer.update(cx, |buffer, cx| {
8905 buffer.finalize_last_transaction(cx);
8906 if self.leader_peer_id.is_none() {
8907 buffer.set_active_selections(
8908 &self.selections.disjoint_anchors(),
8909 self.selections.line_mode,
8910 self.cursor_shape,
8911 cx,
8912 );
8913 }
8914 });
8915 }
8916 }
8917
8918 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8919 let blurred_event = EditorBlurred(cx.handle());
8920 cx.emit_global(blurred_event);
8921 self.focused = false;
8922 self.blink_manager.update(cx, BlinkManager::disable);
8923 self.buffer
8924 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8925 self.hide_context_menu(cx);
8926 hide_hover(self, cx);
8927 cx.emit(Event::Blurred);
8928 cx.notify();
8929 }
8930
8931 fn modifiers_changed(
8932 &mut self,
8933 event: &gpui::platform::ModifiersChangedEvent,
8934 cx: &mut ViewContext<Self>,
8935 ) -> bool {
8936 let pending_selection = self.has_pending_selection();
8937
8938 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
8939 if event.cmd && !pending_selection {
8940 let point = point.clone();
8941 let snapshot = self.snapshot(cx);
8942 let kind = point.definition_kind(event.shift);
8943
8944 show_link_definition(kind, self, point, snapshot, cx);
8945 return false;
8946 }
8947 }
8948
8949 {
8950 if self.link_go_to_definition_state.symbol_range.is_some()
8951 || !self.link_go_to_definition_state.definitions.is_empty()
8952 {
8953 self.link_go_to_definition_state.symbol_range.take();
8954 self.link_go_to_definition_state.definitions.clear();
8955 cx.notify();
8956 }
8957
8958 self.link_go_to_definition_state.task = None;
8959
8960 self.clear_highlights::<LinkGoToDefinitionState>(cx);
8961 }
8962
8963 false
8964 }
8965
8966 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8967 Self::reset_to_default_keymap_context(keymap);
8968 let mode = match self.mode {
8969 EditorMode::SingleLine => "single_line",
8970 EditorMode::AutoHeight { .. } => "auto_height",
8971 EditorMode::Full => "full",
8972 };
8973 keymap.add_key("mode", mode);
8974 if self.pending_rename.is_some() {
8975 keymap.add_identifier("renaming");
8976 }
8977 if self.context_menu_visible() {
8978 match self.context_menu.as_ref() {
8979 Some(ContextMenu::Completions(_)) => {
8980 keymap.add_identifier("menu");
8981 keymap.add_identifier("showing_completions")
8982 }
8983 Some(ContextMenu::CodeActions(_)) => {
8984 keymap.add_identifier("menu");
8985 keymap.add_identifier("showing_code_actions")
8986 }
8987 None => {}
8988 }
8989 }
8990
8991 for layer in self.keymap_context_layers.values() {
8992 keymap.extend(layer);
8993 }
8994
8995 if let Some(extension) = self
8996 .buffer
8997 .read(cx)
8998 .as_singleton()
8999 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
9000 {
9001 keymap.add_key("extension", extension.to_string());
9002 }
9003 }
9004
9005 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
9006 Some(
9007 self.buffer
9008 .read(cx)
9009 .read(cx)
9010 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
9011 .collect(),
9012 )
9013 }
9014
9015 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9016 // Prevent the IME menu from appearing when holding down an alphabetic key
9017 // while input is disabled.
9018 if !self.input_enabled {
9019 return None;
9020 }
9021
9022 let range = self.selections.newest::<OffsetUtf16>(cx).range();
9023 Some(range.start.0..range.end.0)
9024 }
9025
9026 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
9027 let snapshot = self.buffer.read(cx).read(cx);
9028 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
9029 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
9030 }
9031
9032 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
9033 self.clear_highlights::<InputComposition>(cx);
9034 self.ime_transaction.take();
9035 }
9036
9037 fn replace_text_in_range(
9038 &mut self,
9039 range_utf16: Option<Range<usize>>,
9040 text: &str,
9041 cx: &mut ViewContext<Self>,
9042 ) {
9043 if !self.input_enabled {
9044 cx.emit(Event::InputIgnored { text: text.into() });
9045 return;
9046 }
9047
9048 self.transact(cx, |this, cx| {
9049 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
9050 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9051 Some(this.selection_replacement_ranges(range_utf16, cx))
9052 } else {
9053 this.marked_text_ranges(cx)
9054 };
9055
9056 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
9057 let newest_selection_id = this.selections.newest_anchor().id;
9058 this.selections
9059 .all::<OffsetUtf16>(cx)
9060 .iter()
9061 .zip(ranges_to_replace.iter())
9062 .find_map(|(selection, range)| {
9063 if selection.id == newest_selection_id {
9064 Some(
9065 (range.start.0 as isize - selection.head().0 as isize)
9066 ..(range.end.0 as isize - selection.head().0 as isize),
9067 )
9068 } else {
9069 None
9070 }
9071 })
9072 });
9073
9074 cx.emit(Event::InputHandled {
9075 utf16_range_to_replace: range_to_replace,
9076 text: text.into(),
9077 });
9078
9079 if let Some(new_selected_ranges) = new_selected_ranges {
9080 this.change_selections(None, cx, |selections| {
9081 selections.select_ranges(new_selected_ranges)
9082 });
9083 }
9084
9085 this.handle_input(text, cx);
9086 });
9087
9088 if let Some(transaction) = self.ime_transaction {
9089 self.buffer.update(cx, |buffer, cx| {
9090 buffer.group_until_transaction(transaction, cx);
9091 });
9092 }
9093
9094 self.unmark_text(cx);
9095 }
9096
9097 fn replace_and_mark_text_in_range(
9098 &mut self,
9099 range_utf16: Option<Range<usize>>,
9100 text: &str,
9101 new_selected_range_utf16: Option<Range<usize>>,
9102 cx: &mut ViewContext<Self>,
9103 ) {
9104 if !self.input_enabled {
9105 cx.emit(Event::InputIgnored { text: text.into() });
9106 return;
9107 }
9108
9109 let transaction = self.transact(cx, |this, cx| {
9110 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
9111 let snapshot = this.buffer.read(cx).read(cx);
9112 if let Some(relative_range_utf16) = range_utf16.as_ref() {
9113 for marked_range in &mut marked_ranges {
9114 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
9115 marked_range.start.0 += relative_range_utf16.start;
9116 marked_range.start =
9117 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
9118 marked_range.end =
9119 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
9120 }
9121 }
9122 Some(marked_ranges)
9123 } else if let Some(range_utf16) = range_utf16 {
9124 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
9125 Some(this.selection_replacement_ranges(range_utf16, cx))
9126 } else {
9127 None
9128 };
9129
9130 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
9131 let newest_selection_id = this.selections.newest_anchor().id;
9132 this.selections
9133 .all::<OffsetUtf16>(cx)
9134 .iter()
9135 .zip(ranges_to_replace.iter())
9136 .find_map(|(selection, range)| {
9137 if selection.id == newest_selection_id {
9138 Some(
9139 (range.start.0 as isize - selection.head().0 as isize)
9140 ..(range.end.0 as isize - selection.head().0 as isize),
9141 )
9142 } else {
9143 None
9144 }
9145 })
9146 });
9147
9148 cx.emit(Event::InputHandled {
9149 utf16_range_to_replace: range_to_replace,
9150 text: text.into(),
9151 });
9152
9153 if let Some(ranges) = ranges_to_replace {
9154 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9155 }
9156
9157 let marked_ranges = {
9158 let snapshot = this.buffer.read(cx).read(cx);
9159 this.selections
9160 .disjoint_anchors()
9161 .iter()
9162 .map(|selection| {
9163 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9164 })
9165 .collect::<Vec<_>>()
9166 };
9167
9168 if text.is_empty() {
9169 this.unmark_text(cx);
9170 } else {
9171 this.highlight_text::<InputComposition>(
9172 marked_ranges.clone(),
9173 this.style(cx).composition_mark,
9174 cx,
9175 );
9176 }
9177
9178 this.handle_input(text, cx);
9179
9180 if let Some(new_selected_range) = new_selected_range_utf16 {
9181 let snapshot = this.buffer.read(cx).read(cx);
9182 let new_selected_ranges = marked_ranges
9183 .into_iter()
9184 .map(|marked_range| {
9185 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9186 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9187 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9188 snapshot.clip_offset_utf16(new_start, Bias::Left)
9189 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9190 })
9191 .collect::<Vec<_>>();
9192
9193 drop(snapshot);
9194 this.change_selections(None, cx, |selections| {
9195 selections.select_ranges(new_selected_ranges)
9196 });
9197 }
9198 });
9199
9200 self.ime_transaction = self.ime_transaction.or(transaction);
9201 if let Some(transaction) = self.ime_transaction {
9202 self.buffer.update(cx, |buffer, cx| {
9203 buffer.group_until_transaction(transaction, cx);
9204 });
9205 }
9206
9207 if self.text_highlights::<InputComposition>(cx).is_none() {
9208 self.ime_transaction.take();
9209 }
9210 }
9211}
9212
9213fn build_style(
9214 settings: &ThemeSettings,
9215 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9216 override_text_style: Option<&OverrideTextStyle>,
9217 cx: &AppContext,
9218) -> EditorStyle {
9219 let font_cache = cx.font_cache();
9220 let line_height_scalar = settings.line_height();
9221 let theme_id = settings.theme.meta.id;
9222 let mut theme = settings.theme.editor.clone();
9223 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9224 let field_editor_theme = get_field_editor_theme(&settings.theme);
9225 theme.text_color = field_editor_theme.text.color;
9226 theme.selection = field_editor_theme.selection;
9227 theme.background = field_editor_theme
9228 .container
9229 .background_color
9230 .unwrap_or_default();
9231 EditorStyle {
9232 text: field_editor_theme.text,
9233 placeholder_text: field_editor_theme.placeholder_text,
9234 line_height_scalar,
9235 theme,
9236 theme_id,
9237 }
9238 } else {
9239 let font_family_id = settings.buffer_font_family;
9240 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9241 let font_properties = Default::default();
9242 let font_id = font_cache
9243 .select_font(font_family_id, &font_properties)
9244 .unwrap();
9245 let font_size = settings.buffer_font_size(cx);
9246 EditorStyle {
9247 text: TextStyle {
9248 color: settings.theme.editor.text_color,
9249 font_family_name,
9250 font_family_id,
9251 font_id,
9252 font_size,
9253 font_properties,
9254 underline: Default::default(),
9255 soft_wrap: false,
9256 },
9257 placeholder_text: None,
9258 line_height_scalar,
9259 theme,
9260 theme_id,
9261 }
9262 };
9263
9264 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9265 if let Some(highlighted) = style
9266 .text
9267 .clone()
9268 .highlight(highlight_style, font_cache)
9269 .log_err()
9270 {
9271 style.text = highlighted;
9272 }
9273 }
9274
9275 style
9276}
9277
9278trait SelectionExt {
9279 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9280 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9281 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9282 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9283 -> Range<u32>;
9284}
9285
9286impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9287 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9288 let start = self.start.to_point(buffer);
9289 let end = self.end.to_point(buffer);
9290 if self.reversed {
9291 end..start
9292 } else {
9293 start..end
9294 }
9295 }
9296
9297 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9298 let start = self.start.to_offset(buffer);
9299 let end = self.end.to_offset(buffer);
9300 if self.reversed {
9301 end..start
9302 } else {
9303 start..end
9304 }
9305 }
9306
9307 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9308 let start = self
9309 .start
9310 .to_point(&map.buffer_snapshot)
9311 .to_display_point(map);
9312 let end = self
9313 .end
9314 .to_point(&map.buffer_snapshot)
9315 .to_display_point(map);
9316 if self.reversed {
9317 end..start
9318 } else {
9319 start..end
9320 }
9321 }
9322
9323 fn spanned_rows(
9324 &self,
9325 include_end_if_at_line_start: bool,
9326 map: &DisplaySnapshot,
9327 ) -> Range<u32> {
9328 let start = self.start.to_point(&map.buffer_snapshot);
9329 let mut end = self.end.to_point(&map.buffer_snapshot);
9330 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9331 end.row -= 1;
9332 }
9333
9334 let buffer_start = map.prev_line_boundary(start).0;
9335 let buffer_end = map.next_line_boundary(end).0;
9336 buffer_start.row..buffer_end.row + 1
9337 }
9338}
9339
9340impl<T: InvalidationRegion> InvalidationStack<T> {
9341 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9342 where
9343 S: Clone + ToOffset,
9344 {
9345 while let Some(region) = self.last() {
9346 let all_selections_inside_invalidation_ranges =
9347 if selections.len() == region.ranges().len() {
9348 selections
9349 .iter()
9350 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9351 .all(|(selection, invalidation_range)| {
9352 let head = selection.head().to_offset(buffer);
9353 invalidation_range.start <= head && invalidation_range.end >= head
9354 })
9355 } else {
9356 false
9357 };
9358
9359 if all_selections_inside_invalidation_ranges {
9360 break;
9361 } else {
9362 self.pop();
9363 }
9364 }
9365 }
9366}
9367
9368impl<T> Default for InvalidationStack<T> {
9369 fn default() -> Self {
9370 Self(Default::default())
9371 }
9372}
9373
9374impl<T> Deref for InvalidationStack<T> {
9375 type Target = Vec<T>;
9376
9377 fn deref(&self) -> &Self::Target {
9378 &self.0
9379 }
9380}
9381
9382impl<T> DerefMut for InvalidationStack<T> {
9383 fn deref_mut(&mut self) -> &mut Self::Target {
9384 &mut self.0
9385 }
9386}
9387
9388impl InvalidationRegion for SnippetState {
9389 fn ranges(&self) -> &[Range<Anchor>] {
9390 &self.ranges[self.active_index]
9391 }
9392}
9393
9394impl Deref for EditorStyle {
9395 type Target = theme::Editor;
9396
9397 fn deref(&self) -> &Self::Target {
9398 &self.theme
9399 }
9400}
9401
9402pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9403 let mut highlighted_lines = Vec::new();
9404
9405 for (index, line) in diagnostic.message.lines().enumerate() {
9406 let line = match &diagnostic.source {
9407 Some(source) if index == 0 => {
9408 let source_highlight = Vec::from_iter(0..source.len());
9409 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9410 }
9411
9412 _ => highlight_diagnostic_message(Vec::new(), line),
9413 };
9414 highlighted_lines.push(line);
9415 }
9416 let message = diagnostic.message;
9417 Arc::new(move |cx: &mut BlockContext| {
9418 let message = message.clone();
9419 let settings = settings::get::<ThemeSettings>(cx);
9420 let tooltip_style = settings.theme.tooltip.clone();
9421 let theme = &settings.theme.editor;
9422 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9423 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9424 let anchor_x = cx.anchor_x;
9425 enum BlockContextToolip {}
9426 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9427 Flex::column()
9428 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9429 Label::new(
9430 line.clone(),
9431 style.message.clone().with_font_size(font_size),
9432 )
9433 .with_highlights(highlights.clone())
9434 .contained()
9435 .with_margin_left(anchor_x)
9436 }))
9437 .aligned()
9438 .left()
9439 .into_any()
9440 })
9441 .with_cursor_style(CursorStyle::PointingHand)
9442 .on_click(MouseButton::Left, move |_, _, cx| {
9443 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9444 })
9445 // We really need to rethink this ID system...
9446 .with_tooltip::<BlockContextToolip>(
9447 cx.block_id,
9448 "Copy diagnostic message",
9449 None,
9450 tooltip_style,
9451 cx,
9452 )
9453 .into_any()
9454 })
9455}
9456
9457pub fn highlight_diagnostic_message(
9458 initial_highlights: Vec<usize>,
9459 message: &str,
9460) -> (String, Vec<usize>) {
9461 let mut message_without_backticks = String::new();
9462 let mut prev_offset = 0;
9463 let mut inside_block = false;
9464 let mut highlights = initial_highlights;
9465 for (match_ix, (offset, _)) in message
9466 .match_indices('`')
9467 .chain([(message.len(), "")])
9468 .enumerate()
9469 {
9470 message_without_backticks.push_str(&message[prev_offset..offset]);
9471 if inside_block {
9472 highlights.extend(prev_offset - match_ix..offset - match_ix);
9473 }
9474
9475 inside_block = !inside_block;
9476 prev_offset = offset + 1;
9477 }
9478
9479 (message_without_backticks, highlights)
9480}
9481
9482pub fn diagnostic_style(
9483 severity: DiagnosticSeverity,
9484 valid: bool,
9485 theme: &theme::Editor,
9486) -> DiagnosticStyle {
9487 match (severity, valid) {
9488 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9489 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9490 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9491 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9492 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9493 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9494 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9495 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9496 _ => theme.invalid_hint_diagnostic.clone(),
9497 }
9498}
9499
9500pub fn combine_syntax_and_fuzzy_match_highlights(
9501 text: &str,
9502 default_style: HighlightStyle,
9503 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9504 match_indices: &[usize],
9505) -> Vec<(Range<usize>, HighlightStyle)> {
9506 let mut result = Vec::new();
9507 let mut match_indices = match_indices.iter().copied().peekable();
9508
9509 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9510 {
9511 syntax_highlight.weight = None;
9512
9513 // Add highlights for any fuzzy match characters before the next
9514 // syntax highlight range.
9515 while let Some(&match_index) = match_indices.peek() {
9516 if match_index >= range.start {
9517 break;
9518 }
9519 match_indices.next();
9520 let end_index = char_ix_after(match_index, text);
9521 let mut match_style = default_style;
9522 match_style.weight = Some(fonts::Weight::BOLD);
9523 result.push((match_index..end_index, match_style));
9524 }
9525
9526 if range.start == usize::MAX {
9527 break;
9528 }
9529
9530 // Add highlights for any fuzzy match characters within the
9531 // syntax highlight range.
9532 let mut offset = range.start;
9533 while let Some(&match_index) = match_indices.peek() {
9534 if match_index >= range.end {
9535 break;
9536 }
9537
9538 match_indices.next();
9539 if match_index > offset {
9540 result.push((offset..match_index, syntax_highlight));
9541 }
9542
9543 let mut end_index = char_ix_after(match_index, text);
9544 while let Some(&next_match_index) = match_indices.peek() {
9545 if next_match_index == end_index && next_match_index < range.end {
9546 end_index = char_ix_after(next_match_index, text);
9547 match_indices.next();
9548 } else {
9549 break;
9550 }
9551 }
9552
9553 let mut match_style = syntax_highlight;
9554 match_style.weight = Some(fonts::Weight::BOLD);
9555 result.push((match_index..end_index, match_style));
9556 offset = end_index;
9557 }
9558
9559 if offset < range.end {
9560 result.push((offset..range.end, syntax_highlight));
9561 }
9562 }
9563
9564 fn char_ix_after(ix: usize, text: &str) -> usize {
9565 ix + text[ix..].chars().next().unwrap().len_utf8()
9566 }
9567
9568 result
9569}
9570
9571pub fn styled_runs_for_code_label<'a>(
9572 label: &'a CodeLabel,
9573 syntax_theme: &'a theme::SyntaxTheme,
9574) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9575 let fade_out = HighlightStyle {
9576 fade_out: Some(0.35),
9577 ..Default::default()
9578 };
9579
9580 let mut prev_end = label.filter_range.end;
9581 label
9582 .runs
9583 .iter()
9584 .enumerate()
9585 .flat_map(move |(ix, (range, highlight_id))| {
9586 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9587 style
9588 } else {
9589 return Default::default();
9590 };
9591 let mut muted_style = style;
9592 muted_style.highlight(fade_out);
9593
9594 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9595 if range.start >= label.filter_range.end {
9596 if range.start > prev_end {
9597 runs.push((prev_end..range.start, fade_out));
9598 }
9599 runs.push((range.clone(), muted_style));
9600 } else if range.end <= label.filter_range.end {
9601 runs.push((range.clone(), style));
9602 } else {
9603 runs.push((range.start..label.filter_range.end, style));
9604 runs.push((label.filter_range.end..range.end, muted_style));
9605 }
9606 prev_end = cmp::max(prev_end, range.end);
9607
9608 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9609 runs.push((prev_end..label.text.len(), fade_out));
9610 }
9611
9612 runs
9613 })
9614}
9615
9616pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9617 let mut index = 0;
9618 let mut codepoints = text.char_indices().peekable();
9619
9620 std::iter::from_fn(move || {
9621 let start_index = index;
9622 while let Some((new_index, codepoint)) = codepoints.next() {
9623 index = new_index + codepoint.len_utf8();
9624 let current_upper = codepoint.is_uppercase();
9625 let next_upper = codepoints
9626 .peek()
9627 .map(|(_, c)| c.is_uppercase())
9628 .unwrap_or(false);
9629
9630 if !current_upper && next_upper {
9631 return Some(&text[start_index..index]);
9632 }
9633 }
9634
9635 index = text.len();
9636 if start_index < text.len() {
9637 return Some(&text[start_index..]);
9638 }
9639 None
9640 })
9641 .flat_map(|word| word.split_inclusive('_'))
9642 .flat_map(|word| word.split_inclusive('-'))
9643}
9644
9645trait RangeToAnchorExt {
9646 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9647}
9648
9649impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9650 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9651 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9652 }
9653}