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