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