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