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