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