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