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