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| text.to_case(Case::Title))
4657 }
4658
4659 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4660 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4661 }
4662
4663 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4664 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4665 }
4666
4667 pub fn convert_to_upper_camel_case(
4668 &mut self,
4669 _: &ConvertToUpperCamelCase,
4670 cx: &mut ViewContext<Self>,
4671 ) {
4672 self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel))
4673 }
4674
4675 pub fn convert_to_lower_camel_case(
4676 &mut self,
4677 _: &ConvertToLowerCamelCase,
4678 cx: &mut ViewContext<Self>,
4679 ) {
4680 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4681 }
4682
4683 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4684 where
4685 Fn: FnMut(&str) -> String,
4686 {
4687 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4688 let buffer = self.buffer.read(cx).snapshot(cx);
4689
4690 let mut new_selections = Vec::new();
4691 let mut edits = Vec::new();
4692 let mut selection_adjustment = 0i32;
4693
4694 for selection in self.selections.all::<usize>(cx) {
4695 let selection_is_empty = selection.is_empty();
4696
4697 let (start, end) = if selection_is_empty {
4698 let word_range = movement::surrounding_word(
4699 &display_map,
4700 selection.start.to_display_point(&display_map),
4701 );
4702 let start = word_range.start.to_offset(&display_map, Bias::Left);
4703 let end = word_range.end.to_offset(&display_map, Bias::Left);
4704 (start, end)
4705 } else {
4706 (selection.start, selection.end)
4707 };
4708
4709 let text = buffer.text_for_range(start..end).collect::<String>();
4710 let old_length = text.len() as i32;
4711 let text = callback(&text);
4712
4713 new_selections.push(Selection {
4714 start: (start as i32 - selection_adjustment) as usize,
4715 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4716 goal: SelectionGoal::None,
4717 ..selection
4718 });
4719
4720 selection_adjustment += old_length - text.len() as i32;
4721
4722 edits.push((start..end, text));
4723 }
4724
4725 self.transact(cx, |this, cx| {
4726 this.buffer.update(cx, |buffer, cx| {
4727 buffer.edit(edits, None, cx);
4728 });
4729
4730 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4731 s.select(new_selections);
4732 });
4733
4734 this.request_autoscroll(Autoscroll::fit(), cx);
4735 });
4736 }
4737
4738 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4739 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4740 let buffer = &display_map.buffer_snapshot;
4741 let selections = self.selections.all::<Point>(cx);
4742
4743 let mut edits = Vec::new();
4744 let mut selections_iter = selections.iter().peekable();
4745 while let Some(selection) = selections_iter.next() {
4746 // Avoid duplicating the same lines twice.
4747 let mut rows = selection.spanned_rows(false, &display_map);
4748
4749 while let Some(next_selection) = selections_iter.peek() {
4750 let next_rows = next_selection.spanned_rows(false, &display_map);
4751 if next_rows.start < rows.end {
4752 rows.end = next_rows.end;
4753 selections_iter.next().unwrap();
4754 } else {
4755 break;
4756 }
4757 }
4758
4759 // Copy the text from the selected row region and splice it at the start of the region.
4760 let start = Point::new(rows.start, 0);
4761 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4762 let text = buffer
4763 .text_for_range(start..end)
4764 .chain(Some("\n"))
4765 .collect::<String>();
4766 edits.push((start..start, text));
4767 }
4768
4769 self.transact(cx, |this, cx| {
4770 this.buffer.update(cx, |buffer, cx| {
4771 buffer.edit(edits, None, cx);
4772 });
4773
4774 this.request_autoscroll(Autoscroll::fit(), cx);
4775 });
4776 }
4777
4778 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4779 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4780 let buffer = self.buffer.read(cx).snapshot(cx);
4781
4782 let mut edits = Vec::new();
4783 let mut unfold_ranges = Vec::new();
4784 let mut refold_ranges = Vec::new();
4785
4786 let selections = self.selections.all::<Point>(cx);
4787 let mut selections = selections.iter().peekable();
4788 let mut contiguous_row_selections = Vec::new();
4789 let mut new_selections = Vec::new();
4790
4791 while let Some(selection) = selections.next() {
4792 // Find all the selections that span a contiguous row range
4793 let (start_row, end_row) = consume_contiguous_rows(
4794 &mut contiguous_row_selections,
4795 selection,
4796 &display_map,
4797 &mut selections,
4798 );
4799
4800 // Move the text spanned by the row range to be before the line preceding the row range
4801 if start_row > 0 {
4802 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4803 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4804 let insertion_point = display_map
4805 .prev_line_boundary(Point::new(start_row - 1, 0))
4806 .0;
4807
4808 // Don't move lines across excerpts
4809 if buffer
4810 .excerpt_boundaries_in_range((
4811 Bound::Excluded(insertion_point),
4812 Bound::Included(range_to_move.end),
4813 ))
4814 .next()
4815 .is_none()
4816 {
4817 let text = buffer
4818 .text_for_range(range_to_move.clone())
4819 .flat_map(|s| s.chars())
4820 .skip(1)
4821 .chain(['\n'])
4822 .collect::<String>();
4823
4824 edits.push((
4825 buffer.anchor_after(range_to_move.start)
4826 ..buffer.anchor_before(range_to_move.end),
4827 String::new(),
4828 ));
4829 let insertion_anchor = buffer.anchor_after(insertion_point);
4830 edits.push((insertion_anchor..insertion_anchor, text));
4831
4832 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4833
4834 // Move selections up
4835 new_selections.extend(contiguous_row_selections.drain(..).map(
4836 |mut selection| {
4837 selection.start.row -= row_delta;
4838 selection.end.row -= row_delta;
4839 selection
4840 },
4841 ));
4842
4843 // Move folds up
4844 unfold_ranges.push(range_to_move.clone());
4845 for fold in display_map.folds_in_range(
4846 buffer.anchor_before(range_to_move.start)
4847 ..buffer.anchor_after(range_to_move.end),
4848 ) {
4849 let mut start = fold.start.to_point(&buffer);
4850 let mut end = fold.end.to_point(&buffer);
4851 start.row -= row_delta;
4852 end.row -= row_delta;
4853 refold_ranges.push(start..end);
4854 }
4855 }
4856 }
4857
4858 // If we didn't move line(s), preserve the existing selections
4859 new_selections.append(&mut contiguous_row_selections);
4860 }
4861
4862 self.transact(cx, |this, cx| {
4863 this.unfold_ranges(unfold_ranges, true, true, cx);
4864 this.buffer.update(cx, |buffer, cx| {
4865 for (range, text) in edits {
4866 buffer.edit([(range, text)], None, cx);
4867 }
4868 });
4869 this.fold_ranges(refold_ranges, true, cx);
4870 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4871 s.select(new_selections);
4872 })
4873 });
4874 }
4875
4876 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4877 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4878 let buffer = self.buffer.read(cx).snapshot(cx);
4879
4880 let mut edits = Vec::new();
4881 let mut unfold_ranges = Vec::new();
4882 let mut refold_ranges = Vec::new();
4883
4884 let selections = self.selections.all::<Point>(cx);
4885 let mut selections = selections.iter().peekable();
4886 let mut contiguous_row_selections = Vec::new();
4887 let mut new_selections = Vec::new();
4888
4889 while let Some(selection) = selections.next() {
4890 // Find all the selections that span a contiguous row range
4891 let (start_row, end_row) = consume_contiguous_rows(
4892 &mut contiguous_row_selections,
4893 selection,
4894 &display_map,
4895 &mut selections,
4896 );
4897
4898 // Move the text spanned by the row range to be after the last line of the row range
4899 if end_row <= buffer.max_point().row {
4900 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4901 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4902
4903 // Don't move lines across excerpt boundaries
4904 if buffer
4905 .excerpt_boundaries_in_range((
4906 Bound::Excluded(range_to_move.start),
4907 Bound::Included(insertion_point),
4908 ))
4909 .next()
4910 .is_none()
4911 {
4912 let mut text = String::from("\n");
4913 text.extend(buffer.text_for_range(range_to_move.clone()));
4914 text.pop(); // Drop trailing newline
4915 edits.push((
4916 buffer.anchor_after(range_to_move.start)
4917 ..buffer.anchor_before(range_to_move.end),
4918 String::new(),
4919 ));
4920 let insertion_anchor = buffer.anchor_after(insertion_point);
4921 edits.push((insertion_anchor..insertion_anchor, text));
4922
4923 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4924
4925 // Move selections down
4926 new_selections.extend(contiguous_row_selections.drain(..).map(
4927 |mut selection| {
4928 selection.start.row += row_delta;
4929 selection.end.row += row_delta;
4930 selection
4931 },
4932 ));
4933
4934 // Move folds down
4935 unfold_ranges.push(range_to_move.clone());
4936 for fold in display_map.folds_in_range(
4937 buffer.anchor_before(range_to_move.start)
4938 ..buffer.anchor_after(range_to_move.end),
4939 ) {
4940 let mut start = fold.start.to_point(&buffer);
4941 let mut end = fold.end.to_point(&buffer);
4942 start.row += row_delta;
4943 end.row += row_delta;
4944 refold_ranges.push(start..end);
4945 }
4946 }
4947 }
4948
4949 // If we didn't move line(s), preserve the existing selections
4950 new_selections.append(&mut contiguous_row_selections);
4951 }
4952
4953 self.transact(cx, |this, cx| {
4954 this.unfold_ranges(unfold_ranges, true, true, cx);
4955 this.buffer.update(cx, |buffer, cx| {
4956 for (range, text) in edits {
4957 buffer.edit([(range, text)], None, cx);
4958 }
4959 });
4960 this.fold_ranges(refold_ranges, true, cx);
4961 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4962 });
4963 }
4964
4965 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4966 self.transact(cx, |this, cx| {
4967 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4968 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4969 let line_mode = s.line_mode;
4970 s.move_with(|display_map, selection| {
4971 if !selection.is_empty() || line_mode {
4972 return;
4973 }
4974
4975 let mut head = selection.head();
4976 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4977 if head.column() == display_map.line_len(head.row()) {
4978 transpose_offset = display_map
4979 .buffer_snapshot
4980 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4981 }
4982
4983 if transpose_offset == 0 {
4984 return;
4985 }
4986
4987 *head.column_mut() += 1;
4988 head = display_map.clip_point(head, Bias::Right);
4989 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4990
4991 let transpose_start = display_map
4992 .buffer_snapshot
4993 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4994 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4995 let transpose_end = display_map
4996 .buffer_snapshot
4997 .clip_offset(transpose_offset + 1, Bias::Right);
4998 if let Some(ch) =
4999 display_map.buffer_snapshot.chars_at(transpose_start).next()
5000 {
5001 edits.push((transpose_start..transpose_offset, String::new()));
5002 edits.push((transpose_end..transpose_end, ch.to_string()));
5003 }
5004 }
5005 });
5006 edits
5007 });
5008 this.buffer
5009 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
5010 let selections = this.selections.all::<usize>(cx);
5011 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5012 s.select(selections);
5013 });
5014 });
5015 }
5016
5017 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
5018 let mut text = String::new();
5019 let buffer = self.buffer.read(cx).snapshot(cx);
5020 let mut selections = self.selections.all::<Point>(cx);
5021 let mut clipboard_selections = Vec::with_capacity(selections.len());
5022 {
5023 let max_point = buffer.max_point();
5024 let mut is_first = true;
5025 for selection in &mut selections {
5026 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5027 if is_entire_line {
5028 selection.start = Point::new(selection.start.row, 0);
5029 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
5030 selection.goal = SelectionGoal::None;
5031 }
5032 if is_first {
5033 is_first = false;
5034 } else {
5035 text += "\n";
5036 }
5037 let mut len = 0;
5038 for chunk in buffer.text_for_range(selection.start..selection.end) {
5039 text.push_str(chunk);
5040 len += chunk.len();
5041 }
5042 clipboard_selections.push(ClipboardSelection {
5043 len,
5044 is_entire_line,
5045 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
5046 });
5047 }
5048 }
5049
5050 self.transact(cx, |this, cx| {
5051 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5052 s.select(selections);
5053 });
5054 this.insert("", cx);
5055 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5056 });
5057 }
5058
5059 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
5060 let selections = self.selections.all::<Point>(cx);
5061 let buffer = self.buffer.read(cx).read(cx);
5062 let mut text = String::new();
5063
5064 let mut clipboard_selections = Vec::with_capacity(selections.len());
5065 {
5066 let max_point = buffer.max_point();
5067 let mut is_first = true;
5068 for selection in selections.iter() {
5069 let mut start = selection.start;
5070 let mut end = selection.end;
5071 let is_entire_line = selection.is_empty() || self.selections.line_mode;
5072 if is_entire_line {
5073 start = Point::new(start.row, 0);
5074 end = cmp::min(max_point, Point::new(end.row + 1, 0));
5075 }
5076 if is_first {
5077 is_first = false;
5078 } else {
5079 text += "\n";
5080 }
5081 let mut len = 0;
5082 for chunk in buffer.text_for_range(start..end) {
5083 text.push_str(chunk);
5084 len += chunk.len();
5085 }
5086 clipboard_selections.push(ClipboardSelection {
5087 len,
5088 is_entire_line,
5089 first_line_indent: buffer.indent_size_for_line(start.row).len,
5090 });
5091 }
5092 }
5093
5094 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
5095 }
5096
5097 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
5098 self.transact(cx, |this, cx| {
5099 if let Some(item) = cx.read_from_clipboard() {
5100 let clipboard_text = Cow::Borrowed(item.text());
5101 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
5102 let old_selections = this.selections.all::<usize>(cx);
5103 let all_selections_were_entire_line =
5104 clipboard_selections.iter().all(|s| s.is_entire_line);
5105 let first_selection_indent_column =
5106 clipboard_selections.first().map(|s| s.first_line_indent);
5107 if clipboard_selections.len() != old_selections.len() {
5108 clipboard_selections.drain(..);
5109 }
5110
5111 this.buffer.update(cx, |buffer, cx| {
5112 let snapshot = buffer.read(cx);
5113 let mut start_offset = 0;
5114 let mut edits = Vec::new();
5115 let mut original_indent_columns = Vec::new();
5116 let line_mode = this.selections.line_mode;
5117 for (ix, selection) in old_selections.iter().enumerate() {
5118 let to_insert;
5119 let entire_line;
5120 let original_indent_column;
5121 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
5122 let end_offset = start_offset + clipboard_selection.len;
5123 to_insert = &clipboard_text[start_offset..end_offset];
5124 entire_line = clipboard_selection.is_entire_line;
5125 start_offset = end_offset + 1;
5126 original_indent_column =
5127 Some(clipboard_selection.first_line_indent);
5128 } else {
5129 to_insert = clipboard_text.as_str();
5130 entire_line = all_selections_were_entire_line;
5131 original_indent_column = first_selection_indent_column
5132 }
5133
5134 // If the corresponding selection was empty when this slice of the
5135 // clipboard text was written, then the entire line containing the
5136 // selection was copied. If this selection is also currently empty,
5137 // then paste the line before the current line of the buffer.
5138 let range = if selection.is_empty() && !line_mode && entire_line {
5139 let column = selection.start.to_point(&snapshot).column as usize;
5140 let line_start = selection.start - column;
5141 line_start..line_start
5142 } else {
5143 selection.range()
5144 };
5145
5146 edits.push((range, to_insert));
5147 original_indent_columns.extend(original_indent_column);
5148 }
5149 drop(snapshot);
5150
5151 buffer.edit(
5152 edits,
5153 Some(AutoindentMode::Block {
5154 original_indent_columns,
5155 }),
5156 cx,
5157 );
5158 });
5159
5160 let selections = this.selections.all::<usize>(cx);
5161 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5162 } else {
5163 this.insert(&clipboard_text, cx);
5164 }
5165 }
5166 });
5167 }
5168
5169 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
5170 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
5171 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
5172 self.change_selections(None, cx, |s| {
5173 s.select_anchors(selections.to_vec());
5174 });
5175 }
5176 self.request_autoscroll(Autoscroll::fit(), cx);
5177 self.unmark_text(cx);
5178 self.refresh_copilot_suggestions(true, cx);
5179 cx.emit(Event::Edited);
5180 }
5181 }
5182
5183 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
5184 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
5185 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
5186 {
5187 self.change_selections(None, cx, |s| {
5188 s.select_anchors(selections.to_vec());
5189 });
5190 }
5191 self.request_autoscroll(Autoscroll::fit(), cx);
5192 self.unmark_text(cx);
5193 self.refresh_copilot_suggestions(true, cx);
5194 cx.emit(Event::Edited);
5195 }
5196 }
5197
5198 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
5199 self.buffer
5200 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
5201 }
5202
5203 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
5204 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5205 let line_mode = s.line_mode;
5206 s.move_with(|map, selection| {
5207 let cursor = if selection.is_empty() && !line_mode {
5208 movement::left(map, selection.start)
5209 } else {
5210 selection.start
5211 };
5212 selection.collapse_to(cursor, SelectionGoal::None);
5213 });
5214 })
5215 }
5216
5217 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
5218 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5219 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
5220 })
5221 }
5222
5223 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
5224 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5225 let line_mode = s.line_mode;
5226 s.move_with(|map, selection| {
5227 let cursor = if selection.is_empty() && !line_mode {
5228 movement::right(map, selection.end)
5229 } else {
5230 selection.end
5231 };
5232 selection.collapse_to(cursor, SelectionGoal::None)
5233 });
5234 })
5235 }
5236
5237 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
5238 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5239 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
5240 })
5241 }
5242
5243 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
5244 if self.take_rename(true, cx).is_some() {
5245 return;
5246 }
5247
5248 if matches!(self.mode, EditorMode::SingleLine) {
5249 cx.propagate_action();
5250 return;
5251 }
5252
5253 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5254 let line_mode = s.line_mode;
5255 s.move_with(|map, selection| {
5256 if !selection.is_empty() && !line_mode {
5257 selection.goal = SelectionGoal::None;
5258 }
5259 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
5260 selection.collapse_to(cursor, goal);
5261 });
5262 })
5263 }
5264
5265 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
5266 if self.take_rename(true, cx).is_some() {
5267 return;
5268 }
5269
5270 if matches!(self.mode, EditorMode::SingleLine) {
5271 cx.propagate_action();
5272 return;
5273 }
5274
5275 let row_count = if let Some(row_count) = self.visible_line_count() {
5276 row_count as u32 - 1
5277 } else {
5278 return;
5279 };
5280
5281 let autoscroll = if action.center_cursor {
5282 Autoscroll::center()
5283 } else {
5284 Autoscroll::fit()
5285 };
5286
5287 self.change_selections(Some(autoscroll), cx, |s| {
5288 let line_mode = s.line_mode;
5289 s.move_with(|map, selection| {
5290 if !selection.is_empty() && !line_mode {
5291 selection.goal = SelectionGoal::None;
5292 }
5293 let (cursor, goal) =
5294 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
5295 selection.collapse_to(cursor, goal);
5296 });
5297 });
5298 }
5299
5300 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5301 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5302 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5303 })
5304 }
5305
5306 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5307 self.take_rename(true, cx);
5308
5309 if self.mode == EditorMode::SingleLine {
5310 cx.propagate_action();
5311 return;
5312 }
5313
5314 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5315 let line_mode = s.line_mode;
5316 s.move_with(|map, selection| {
5317 if !selection.is_empty() && !line_mode {
5318 selection.goal = SelectionGoal::None;
5319 }
5320 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5321 selection.collapse_to(cursor, goal);
5322 });
5323 });
5324 }
5325
5326 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5327 if self.take_rename(true, cx).is_some() {
5328 return;
5329 }
5330
5331 if self
5332 .context_menu
5333 .as_mut()
5334 .map(|menu| menu.select_last(cx))
5335 .unwrap_or(false)
5336 {
5337 return;
5338 }
5339
5340 if matches!(self.mode, EditorMode::SingleLine) {
5341 cx.propagate_action();
5342 return;
5343 }
5344
5345 let row_count = if let Some(row_count) = self.visible_line_count() {
5346 row_count as u32 - 1
5347 } else {
5348 return;
5349 };
5350
5351 let autoscroll = if action.center_cursor {
5352 Autoscroll::center()
5353 } else {
5354 Autoscroll::fit()
5355 };
5356
5357 self.change_selections(Some(autoscroll), cx, |s| {
5358 let line_mode = s.line_mode;
5359 s.move_with(|map, selection| {
5360 if !selection.is_empty() && !line_mode {
5361 selection.goal = SelectionGoal::None;
5362 }
5363 let (cursor, goal) =
5364 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5365 selection.collapse_to(cursor, goal);
5366 });
5367 });
5368 }
5369
5370 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5371 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5372 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5373 });
5374 }
5375
5376 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
5377 if let Some(context_menu) = self.context_menu.as_mut() {
5378 context_menu.select_first(cx);
5379 }
5380 }
5381
5382 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
5383 if let Some(context_menu) = self.context_menu.as_mut() {
5384 context_menu.select_prev(cx);
5385 }
5386 }
5387
5388 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
5389 if let Some(context_menu) = self.context_menu.as_mut() {
5390 context_menu.select_next(cx);
5391 }
5392 }
5393
5394 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
5395 if let Some(context_menu) = self.context_menu.as_mut() {
5396 context_menu.select_last(cx);
5397 }
5398 }
5399
5400 pub fn move_to_previous_word_start(
5401 &mut self,
5402 _: &MoveToPreviousWordStart,
5403 cx: &mut ViewContext<Self>,
5404 ) {
5405 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5406 s.move_cursors_with(|map, head, _| {
5407 (
5408 movement::previous_word_start(map, head),
5409 SelectionGoal::None,
5410 )
5411 });
5412 })
5413 }
5414
5415 pub fn move_to_previous_subword_start(
5416 &mut self,
5417 _: &MoveToPreviousSubwordStart,
5418 cx: &mut ViewContext<Self>,
5419 ) {
5420 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5421 s.move_cursors_with(|map, head, _| {
5422 (
5423 movement::previous_subword_start(map, head),
5424 SelectionGoal::None,
5425 )
5426 });
5427 })
5428 }
5429
5430 pub fn select_to_previous_word_start(
5431 &mut self,
5432 _: &SelectToPreviousWordStart,
5433 cx: &mut ViewContext<Self>,
5434 ) {
5435 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5436 s.move_heads_with(|map, head, _| {
5437 (
5438 movement::previous_word_start(map, head),
5439 SelectionGoal::None,
5440 )
5441 });
5442 })
5443 }
5444
5445 pub fn select_to_previous_subword_start(
5446 &mut self,
5447 _: &SelectToPreviousSubwordStart,
5448 cx: &mut ViewContext<Self>,
5449 ) {
5450 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5451 s.move_heads_with(|map, head, _| {
5452 (
5453 movement::previous_subword_start(map, head),
5454 SelectionGoal::None,
5455 )
5456 });
5457 })
5458 }
5459
5460 pub fn delete_to_previous_word_start(
5461 &mut self,
5462 _: &DeleteToPreviousWordStart,
5463 cx: &mut ViewContext<Self>,
5464 ) {
5465 self.transact(cx, |this, cx| {
5466 this.select_autoclose_pair(cx);
5467 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5468 let line_mode = s.line_mode;
5469 s.move_with(|map, selection| {
5470 if selection.is_empty() && !line_mode {
5471 let cursor = movement::previous_word_start(map, selection.head());
5472 selection.set_head(cursor, SelectionGoal::None);
5473 }
5474 });
5475 });
5476 this.insert("", cx);
5477 });
5478 }
5479
5480 pub fn delete_to_previous_subword_start(
5481 &mut self,
5482 _: &DeleteToPreviousSubwordStart,
5483 cx: &mut ViewContext<Self>,
5484 ) {
5485 self.transact(cx, |this, cx| {
5486 this.select_autoclose_pair(cx);
5487 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5488 let line_mode = s.line_mode;
5489 s.move_with(|map, selection| {
5490 if selection.is_empty() && !line_mode {
5491 let cursor = movement::previous_subword_start(map, selection.head());
5492 selection.set_head(cursor, SelectionGoal::None);
5493 }
5494 });
5495 });
5496 this.insert("", cx);
5497 });
5498 }
5499
5500 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5501 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5502 s.move_cursors_with(|map, head, _| {
5503 (movement::next_word_end(map, head), SelectionGoal::None)
5504 });
5505 })
5506 }
5507
5508 pub fn move_to_next_subword_end(
5509 &mut self,
5510 _: &MoveToNextSubwordEnd,
5511 cx: &mut ViewContext<Self>,
5512 ) {
5513 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5514 s.move_cursors_with(|map, head, _| {
5515 (movement::next_subword_end(map, head), SelectionGoal::None)
5516 });
5517 })
5518 }
5519
5520 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5521 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5522 s.move_heads_with(|map, head, _| {
5523 (movement::next_word_end(map, head), SelectionGoal::None)
5524 });
5525 })
5526 }
5527
5528 pub fn select_to_next_subword_end(
5529 &mut self,
5530 _: &SelectToNextSubwordEnd,
5531 cx: &mut ViewContext<Self>,
5532 ) {
5533 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5534 s.move_heads_with(|map, head, _| {
5535 (movement::next_subword_end(map, head), SelectionGoal::None)
5536 });
5537 })
5538 }
5539
5540 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5541 self.transact(cx, |this, cx| {
5542 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5543 let line_mode = s.line_mode;
5544 s.move_with(|map, selection| {
5545 if selection.is_empty() && !line_mode {
5546 let cursor = movement::next_word_end(map, selection.head());
5547 selection.set_head(cursor, SelectionGoal::None);
5548 }
5549 });
5550 });
5551 this.insert("", cx);
5552 });
5553 }
5554
5555 pub fn delete_to_next_subword_end(
5556 &mut self,
5557 _: &DeleteToNextSubwordEnd,
5558 cx: &mut ViewContext<Self>,
5559 ) {
5560 self.transact(cx, |this, cx| {
5561 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5562 s.move_with(|map, selection| {
5563 if selection.is_empty() {
5564 let cursor = movement::next_subword_end(map, selection.head());
5565 selection.set_head(cursor, SelectionGoal::None);
5566 }
5567 });
5568 });
5569 this.insert("", cx);
5570 });
5571 }
5572
5573 pub fn move_to_beginning_of_line(
5574 &mut self,
5575 _: &MoveToBeginningOfLine,
5576 cx: &mut ViewContext<Self>,
5577 ) {
5578 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5579 s.move_cursors_with(|map, head, _| {
5580 (
5581 movement::indented_line_beginning(map, head, true),
5582 SelectionGoal::None,
5583 )
5584 });
5585 })
5586 }
5587
5588 pub fn select_to_beginning_of_line(
5589 &mut self,
5590 action: &SelectToBeginningOfLine,
5591 cx: &mut ViewContext<Self>,
5592 ) {
5593 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5594 s.move_heads_with(|map, head, _| {
5595 (
5596 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5597 SelectionGoal::None,
5598 )
5599 });
5600 });
5601 }
5602
5603 pub fn delete_to_beginning_of_line(
5604 &mut self,
5605 _: &DeleteToBeginningOfLine,
5606 cx: &mut ViewContext<Self>,
5607 ) {
5608 self.transact(cx, |this, cx| {
5609 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5610 s.move_with(|_, selection| {
5611 selection.reversed = true;
5612 });
5613 });
5614
5615 this.select_to_beginning_of_line(
5616 &SelectToBeginningOfLine {
5617 stop_at_soft_wraps: false,
5618 },
5619 cx,
5620 );
5621 this.backspace(&Backspace, cx);
5622 });
5623 }
5624
5625 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5626 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5627 s.move_cursors_with(|map, head, _| {
5628 (movement::line_end(map, head, true), SelectionGoal::None)
5629 });
5630 })
5631 }
5632
5633 pub fn select_to_end_of_line(
5634 &mut self,
5635 action: &SelectToEndOfLine,
5636 cx: &mut ViewContext<Self>,
5637 ) {
5638 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5639 s.move_heads_with(|map, head, _| {
5640 (
5641 movement::line_end(map, head, action.stop_at_soft_wraps),
5642 SelectionGoal::None,
5643 )
5644 });
5645 })
5646 }
5647
5648 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5649 self.transact(cx, |this, cx| {
5650 this.select_to_end_of_line(
5651 &SelectToEndOfLine {
5652 stop_at_soft_wraps: false,
5653 },
5654 cx,
5655 );
5656 this.delete(&Delete, cx);
5657 });
5658 }
5659
5660 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, 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.cut(&Cut, cx);
5669 });
5670 }
5671
5672 pub fn move_to_start_of_paragraph(
5673 &mut self,
5674 _: &MoveToStartOfParagraph,
5675 cx: &mut ViewContext<Self>,
5676 ) {
5677 if matches!(self.mode, EditorMode::SingleLine) {
5678 cx.propagate_action();
5679 return;
5680 }
5681
5682 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5683 s.move_with(|map, selection| {
5684 selection.collapse_to(
5685 movement::start_of_paragraph(map, selection.head(), 1),
5686 SelectionGoal::None,
5687 )
5688 });
5689 })
5690 }
5691
5692 pub fn move_to_end_of_paragraph(
5693 &mut self,
5694 _: &MoveToEndOfParagraph,
5695 cx: &mut ViewContext<Self>,
5696 ) {
5697 if matches!(self.mode, EditorMode::SingleLine) {
5698 cx.propagate_action();
5699 return;
5700 }
5701
5702 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5703 s.move_with(|map, selection| {
5704 selection.collapse_to(
5705 movement::end_of_paragraph(map, selection.head(), 1),
5706 SelectionGoal::None,
5707 )
5708 });
5709 })
5710 }
5711
5712 pub fn select_to_start_of_paragraph(
5713 &mut self,
5714 _: &SelectToStartOfParagraph,
5715 cx: &mut ViewContext<Self>,
5716 ) {
5717 if matches!(self.mode, EditorMode::SingleLine) {
5718 cx.propagate_action();
5719 return;
5720 }
5721
5722 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5723 s.move_heads_with(|map, head, _| {
5724 (
5725 movement::start_of_paragraph(map, head, 1),
5726 SelectionGoal::None,
5727 )
5728 });
5729 })
5730 }
5731
5732 pub fn select_to_end_of_paragraph(
5733 &mut self,
5734 _: &SelectToEndOfParagraph,
5735 cx: &mut ViewContext<Self>,
5736 ) {
5737 if matches!(self.mode, EditorMode::SingleLine) {
5738 cx.propagate_action();
5739 return;
5740 }
5741
5742 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5743 s.move_heads_with(|map, head, _| {
5744 (
5745 movement::end_of_paragraph(map, head, 1),
5746 SelectionGoal::None,
5747 )
5748 });
5749 })
5750 }
5751
5752 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5753 if matches!(self.mode, EditorMode::SingleLine) {
5754 cx.propagate_action();
5755 return;
5756 }
5757
5758 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5759 s.select_ranges(vec![0..0]);
5760 });
5761 }
5762
5763 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5764 let mut selection = self.selections.last::<Point>(cx);
5765 selection.set_head(Point::zero(), SelectionGoal::None);
5766
5767 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5768 s.select(vec![selection]);
5769 });
5770 }
5771
5772 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5773 if matches!(self.mode, EditorMode::SingleLine) {
5774 cx.propagate_action();
5775 return;
5776 }
5777
5778 let cursor = self.buffer.read(cx).read(cx).len();
5779 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5780 s.select_ranges(vec![cursor..cursor])
5781 });
5782 }
5783
5784 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5785 self.nav_history = nav_history;
5786 }
5787
5788 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5789 self.nav_history.as_ref()
5790 }
5791
5792 fn push_to_nav_history(
5793 &mut self,
5794 cursor_anchor: Anchor,
5795 new_position: Option<Point>,
5796 cx: &mut ViewContext<Self>,
5797 ) {
5798 if let Some(nav_history) = self.nav_history.as_mut() {
5799 let buffer = self.buffer.read(cx).read(cx);
5800 let cursor_position = cursor_anchor.to_point(&buffer);
5801 let scroll_state = self.scroll_manager.anchor();
5802 let scroll_top_row = scroll_state.top_row(&buffer);
5803 drop(buffer);
5804
5805 if let Some(new_position) = new_position {
5806 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5807 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5808 return;
5809 }
5810 }
5811
5812 nav_history.push(
5813 Some(NavigationData {
5814 cursor_anchor,
5815 cursor_position,
5816 scroll_anchor: scroll_state,
5817 scroll_top_row,
5818 }),
5819 cx,
5820 );
5821 }
5822 }
5823
5824 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5825 let buffer = self.buffer.read(cx).snapshot(cx);
5826 let mut selection = self.selections.first::<usize>(cx);
5827 selection.set_head(buffer.len(), SelectionGoal::None);
5828 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5829 s.select(vec![selection]);
5830 });
5831 }
5832
5833 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5834 let end = self.buffer.read(cx).read(cx).len();
5835 self.change_selections(None, cx, |s| {
5836 s.select_ranges(vec![0..end]);
5837 });
5838 }
5839
5840 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5841 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5842 let mut selections = self.selections.all::<Point>(cx);
5843 let max_point = display_map.buffer_snapshot.max_point();
5844 for selection in &mut selections {
5845 let rows = selection.spanned_rows(true, &display_map);
5846 selection.start = Point::new(rows.start, 0);
5847 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5848 selection.reversed = false;
5849 }
5850 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5851 s.select(selections);
5852 });
5853 }
5854
5855 pub fn split_selection_into_lines(
5856 &mut self,
5857 _: &SplitSelectionIntoLines,
5858 cx: &mut ViewContext<Self>,
5859 ) {
5860 let mut to_unfold = Vec::new();
5861 let mut new_selection_ranges = Vec::new();
5862 {
5863 let selections = self.selections.all::<Point>(cx);
5864 let buffer = self.buffer.read(cx).read(cx);
5865 for selection in selections {
5866 for row in selection.start.row..selection.end.row {
5867 let cursor = Point::new(row, buffer.line_len(row));
5868 new_selection_ranges.push(cursor..cursor);
5869 }
5870 new_selection_ranges.push(selection.end..selection.end);
5871 to_unfold.push(selection.start..selection.end);
5872 }
5873 }
5874 self.unfold_ranges(to_unfold, true, true, cx);
5875 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5876 s.select_ranges(new_selection_ranges);
5877 });
5878 }
5879
5880 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5881 self.add_selection(true, cx);
5882 }
5883
5884 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5885 self.add_selection(false, cx);
5886 }
5887
5888 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5889 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5890 let mut selections = self.selections.all::<Point>(cx);
5891 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5892 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5893 let range = oldest_selection.display_range(&display_map).sorted();
5894 let columns = cmp::min(range.start.column(), range.end.column())
5895 ..cmp::max(range.start.column(), range.end.column());
5896
5897 selections.clear();
5898 let mut stack = Vec::new();
5899 for row in range.start.row()..=range.end.row() {
5900 if let Some(selection) = self.selections.build_columnar_selection(
5901 &display_map,
5902 row,
5903 &columns,
5904 oldest_selection.reversed,
5905 ) {
5906 stack.push(selection.id);
5907 selections.push(selection);
5908 }
5909 }
5910
5911 if above {
5912 stack.reverse();
5913 }
5914
5915 AddSelectionsState { above, stack }
5916 });
5917
5918 let last_added_selection = *state.stack.last().unwrap();
5919 let mut new_selections = Vec::new();
5920 if above == state.above {
5921 let end_row = if above {
5922 0
5923 } else {
5924 display_map.max_point().row()
5925 };
5926
5927 'outer: for selection in selections {
5928 if selection.id == last_added_selection {
5929 let range = selection.display_range(&display_map).sorted();
5930 debug_assert_eq!(range.start.row(), range.end.row());
5931 let mut row = range.start.row();
5932 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5933 {
5934 start..end
5935 } else {
5936 cmp::min(range.start.column(), range.end.column())
5937 ..cmp::max(range.start.column(), range.end.column())
5938 };
5939
5940 while row != end_row {
5941 if above {
5942 row -= 1;
5943 } else {
5944 row += 1;
5945 }
5946
5947 if let Some(new_selection) = self.selections.build_columnar_selection(
5948 &display_map,
5949 row,
5950 &columns,
5951 selection.reversed,
5952 ) {
5953 state.stack.push(new_selection.id);
5954 if above {
5955 new_selections.push(new_selection);
5956 new_selections.push(selection);
5957 } else {
5958 new_selections.push(selection);
5959 new_selections.push(new_selection);
5960 }
5961
5962 continue 'outer;
5963 }
5964 }
5965 }
5966
5967 new_selections.push(selection);
5968 }
5969 } else {
5970 new_selections = selections;
5971 new_selections.retain(|s| s.id != last_added_selection);
5972 state.stack.pop();
5973 }
5974
5975 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5976 s.select(new_selections);
5977 });
5978 if state.stack.len() > 1 {
5979 self.add_selections_state = Some(state);
5980 }
5981 }
5982
5983 pub fn select_next_match_internal(
5984 &mut self,
5985 display_map: &DisplaySnapshot,
5986 replace_newest: bool,
5987 autoscroll: Option<Autoscroll>,
5988 cx: &mut ViewContext<Self>,
5989 ) -> Result<()> {
5990 fn select_next_match_ranges(
5991 this: &mut Editor,
5992 range: Range<usize>,
5993 replace_newest: bool,
5994 auto_scroll: Option<Autoscroll>,
5995 cx: &mut ViewContext<Editor>,
5996 ) {
5997 this.unfold_ranges([range.clone()], false, true, cx);
5998 this.change_selections(auto_scroll, cx, |s| {
5999 if replace_newest {
6000 s.delete(s.newest_anchor().id);
6001 }
6002 s.insert_range(range.clone());
6003 });
6004 }
6005
6006 let buffer = &display_map.buffer_snapshot;
6007 let mut selections = self.selections.all::<usize>(cx);
6008 if let Some(mut select_next_state) = self.select_next_state.take() {
6009 let query = &select_next_state.query;
6010 if !select_next_state.done {
6011 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6012 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6013 let mut next_selected_range = None;
6014
6015 let bytes_after_last_selection =
6016 buffer.bytes_in_range(last_selection.end..buffer.len());
6017 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
6018 let query_matches = query
6019 .stream_find_iter(bytes_after_last_selection)
6020 .map(|result| (last_selection.end, result))
6021 .chain(
6022 query
6023 .stream_find_iter(bytes_before_first_selection)
6024 .map(|result| (0, result)),
6025 );
6026
6027 for (start_offset, query_match) in query_matches {
6028 let query_match = query_match.unwrap(); // can only fail due to I/O
6029 let offset_range =
6030 start_offset + query_match.start()..start_offset + query_match.end();
6031 let display_range = offset_range.start.to_display_point(&display_map)
6032 ..offset_range.end.to_display_point(&display_map);
6033
6034 if !select_next_state.wordwise
6035 || (!movement::is_inside_word(&display_map, display_range.start)
6036 && !movement::is_inside_word(&display_map, display_range.end))
6037 {
6038 if selections
6039 .iter()
6040 .find(|selection| selection.equals(&offset_range))
6041 .is_none()
6042 {
6043 next_selected_range = Some(offset_range);
6044 break;
6045 }
6046 }
6047 }
6048
6049 if let Some(next_selected_range) = next_selected_range {
6050 select_next_match_ranges(
6051 self,
6052 next_selected_range,
6053 replace_newest,
6054 autoscroll,
6055 cx,
6056 );
6057 } else {
6058 select_next_state.done = true;
6059 }
6060 }
6061
6062 self.select_next_state = Some(select_next_state);
6063 } else if selections.len() == 1 {
6064 let selection = selections.last_mut().unwrap();
6065 if selection.start == selection.end {
6066 let word_range = movement::surrounding_word(
6067 &display_map,
6068 selection.start.to_display_point(&display_map),
6069 );
6070 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6071 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6072 selection.goal = SelectionGoal::None;
6073 selection.reversed = false;
6074
6075 let query = buffer
6076 .text_for_range(selection.start..selection.end)
6077 .collect::<String>();
6078
6079 let is_empty = query.is_empty();
6080 let select_state = SelectNextState {
6081 query: AhoCorasick::new(&[query])?,
6082 wordwise: true,
6083 done: is_empty,
6084 };
6085 select_next_match_ranges(
6086 self,
6087 selection.start..selection.end,
6088 replace_newest,
6089 autoscroll,
6090 cx,
6091 );
6092 self.select_next_state = Some(select_state);
6093 } else {
6094 let query = buffer
6095 .text_for_range(selection.start..selection.end)
6096 .collect::<String>();
6097 self.select_next_state = Some(SelectNextState {
6098 query: AhoCorasick::new(&[query])?,
6099 wordwise: false,
6100 done: false,
6101 });
6102 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6103 }
6104 }
6105 Ok(())
6106 }
6107
6108 pub fn select_all_matches(
6109 &mut self,
6110 action: &SelectAllMatches,
6111 cx: &mut ViewContext<Self>,
6112 ) -> Result<()> {
6113 self.push_to_selection_history();
6114 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6115
6116 loop {
6117 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6118
6119 if self
6120 .select_next_state
6121 .as_ref()
6122 .map(|selection_state| selection_state.done)
6123 .unwrap_or(true)
6124 {
6125 break;
6126 }
6127 }
6128
6129 Ok(())
6130 }
6131
6132 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6133 self.push_to_selection_history();
6134 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6135 self.select_next_match_internal(
6136 &display_map,
6137 action.replace_newest,
6138 Some(Autoscroll::newest()),
6139 cx,
6140 )?;
6141 Ok(())
6142 }
6143
6144 pub fn select_previous(
6145 &mut self,
6146 action: &SelectPrevious,
6147 cx: &mut ViewContext<Self>,
6148 ) -> Result<()> {
6149 self.push_to_selection_history();
6150 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6151 let buffer = &display_map.buffer_snapshot;
6152 let mut selections = self.selections.all::<usize>(cx);
6153 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6154 let query = &select_prev_state.query;
6155 if !select_prev_state.done {
6156 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6157 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6158 let mut next_selected_range = None;
6159 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6160 let bytes_before_last_selection =
6161 buffer.reversed_bytes_in_range(0..last_selection.start);
6162 let bytes_after_first_selection =
6163 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6164 let query_matches = query
6165 .stream_find_iter(bytes_before_last_selection)
6166 .map(|result| (last_selection.start, result))
6167 .chain(
6168 query
6169 .stream_find_iter(bytes_after_first_selection)
6170 .map(|result| (buffer.len(), result)),
6171 );
6172 for (end_offset, query_match) in query_matches {
6173 let query_match = query_match.unwrap(); // can only fail due to I/O
6174 let offset_range =
6175 end_offset - query_match.end()..end_offset - query_match.start();
6176 let display_range = offset_range.start.to_display_point(&display_map)
6177 ..offset_range.end.to_display_point(&display_map);
6178
6179 if !select_prev_state.wordwise
6180 || (!movement::is_inside_word(&display_map, display_range.start)
6181 && !movement::is_inside_word(&display_map, display_range.end))
6182 {
6183 next_selected_range = Some(offset_range);
6184 break;
6185 }
6186 }
6187
6188 if let Some(next_selected_range) = next_selected_range {
6189 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6190 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6191 if action.replace_newest {
6192 s.delete(s.newest_anchor().id);
6193 }
6194 s.insert_range(next_selected_range);
6195 });
6196 } else {
6197 select_prev_state.done = true;
6198 }
6199 }
6200
6201 self.select_prev_state = Some(select_prev_state);
6202 } else if selections.len() == 1 {
6203 let selection = selections.last_mut().unwrap();
6204 if selection.start == selection.end {
6205 let word_range = movement::surrounding_word(
6206 &display_map,
6207 selection.start.to_display_point(&display_map),
6208 );
6209 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6210 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6211 selection.goal = SelectionGoal::None;
6212 selection.reversed = false;
6213
6214 let query = buffer
6215 .text_for_range(selection.start..selection.end)
6216 .collect::<String>();
6217 let query = query.chars().rev().collect::<String>();
6218 let select_state = SelectNextState {
6219 query: AhoCorasick::new(&[query])?,
6220 wordwise: true,
6221 done: false,
6222 };
6223 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6224 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6225 s.select(selections);
6226 });
6227 self.select_prev_state = Some(select_state);
6228 } else {
6229 let query = buffer
6230 .text_for_range(selection.start..selection.end)
6231 .collect::<String>();
6232 let query = query.chars().rev().collect::<String>();
6233 self.select_prev_state = Some(SelectNextState {
6234 query: AhoCorasick::new(&[query])?,
6235 wordwise: false,
6236 done: false,
6237 });
6238 self.select_previous(action, cx)?;
6239 }
6240 }
6241 Ok(())
6242 }
6243
6244 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6245 self.transact(cx, |this, cx| {
6246 let mut selections = this.selections.all::<Point>(cx);
6247 let mut edits = Vec::new();
6248 let mut selection_edit_ranges = Vec::new();
6249 let mut last_toggled_row = None;
6250 let snapshot = this.buffer.read(cx).read(cx);
6251 let empty_str: Arc<str> = "".into();
6252 let mut suffixes_inserted = Vec::new();
6253
6254 fn comment_prefix_range(
6255 snapshot: &MultiBufferSnapshot,
6256 row: u32,
6257 comment_prefix: &str,
6258 comment_prefix_whitespace: &str,
6259 ) -> Range<Point> {
6260 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6261
6262 let mut line_bytes = snapshot
6263 .bytes_in_range(start..snapshot.max_point())
6264 .flatten()
6265 .copied();
6266
6267 // If this line currently begins with the line comment prefix, then record
6268 // the range containing the prefix.
6269 if line_bytes
6270 .by_ref()
6271 .take(comment_prefix.len())
6272 .eq(comment_prefix.bytes())
6273 {
6274 // Include any whitespace that matches the comment prefix.
6275 let matching_whitespace_len = line_bytes
6276 .zip(comment_prefix_whitespace.bytes())
6277 .take_while(|(a, b)| a == b)
6278 .count() as u32;
6279 let end = Point::new(
6280 start.row,
6281 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6282 );
6283 start..end
6284 } else {
6285 start..start
6286 }
6287 }
6288
6289 fn comment_suffix_range(
6290 snapshot: &MultiBufferSnapshot,
6291 row: u32,
6292 comment_suffix: &str,
6293 comment_suffix_has_leading_space: bool,
6294 ) -> Range<Point> {
6295 let end = Point::new(row, snapshot.line_len(row));
6296 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6297
6298 let mut line_end_bytes = snapshot
6299 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6300 .flatten()
6301 .copied();
6302
6303 let leading_space_len = if suffix_start_column > 0
6304 && line_end_bytes.next() == Some(b' ')
6305 && comment_suffix_has_leading_space
6306 {
6307 1
6308 } else {
6309 0
6310 };
6311
6312 // If this line currently begins with the line comment prefix, then record
6313 // the range containing the prefix.
6314 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6315 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6316 start..end
6317 } else {
6318 end..end
6319 }
6320 }
6321
6322 // TODO: Handle selections that cross excerpts
6323 for selection in &mut selections {
6324 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6325 let language = if let Some(language) =
6326 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6327 {
6328 language
6329 } else {
6330 continue;
6331 };
6332
6333 selection_edit_ranges.clear();
6334
6335 // If multiple selections contain a given row, avoid processing that
6336 // row more than once.
6337 let mut start_row = selection.start.row;
6338 if last_toggled_row == Some(start_row) {
6339 start_row += 1;
6340 }
6341 let end_row =
6342 if selection.end.row > selection.start.row && selection.end.column == 0 {
6343 selection.end.row - 1
6344 } else {
6345 selection.end.row
6346 };
6347 last_toggled_row = Some(end_row);
6348
6349 if start_row > end_row {
6350 continue;
6351 }
6352
6353 // If the language has line comments, toggle those.
6354 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6355 // Split the comment prefix's trailing whitespace into a separate string,
6356 // as that portion won't be used for detecting if a line is a comment.
6357 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6358 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6359 let mut all_selection_lines_are_comments = true;
6360
6361 for row in start_row..=end_row {
6362 if snapshot.is_line_blank(row) && start_row < end_row {
6363 continue;
6364 }
6365
6366 let prefix_range = comment_prefix_range(
6367 snapshot.deref(),
6368 row,
6369 comment_prefix,
6370 comment_prefix_whitespace,
6371 );
6372 if prefix_range.is_empty() {
6373 all_selection_lines_are_comments = false;
6374 }
6375 selection_edit_ranges.push(prefix_range);
6376 }
6377
6378 if all_selection_lines_are_comments {
6379 edits.extend(
6380 selection_edit_ranges
6381 .iter()
6382 .cloned()
6383 .map(|range| (range, empty_str.clone())),
6384 );
6385 } else {
6386 let min_column = selection_edit_ranges
6387 .iter()
6388 .map(|r| r.start.column)
6389 .min()
6390 .unwrap_or(0);
6391 edits.extend(selection_edit_ranges.iter().map(|range| {
6392 let position = Point::new(range.start.row, min_column);
6393 (position..position, full_comment_prefix.clone())
6394 }));
6395 }
6396 } else if let Some((full_comment_prefix, comment_suffix)) =
6397 language.block_comment_delimiters()
6398 {
6399 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6400 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6401 let prefix_range = comment_prefix_range(
6402 snapshot.deref(),
6403 start_row,
6404 comment_prefix,
6405 comment_prefix_whitespace,
6406 );
6407 let suffix_range = comment_suffix_range(
6408 snapshot.deref(),
6409 end_row,
6410 comment_suffix.trim_start_matches(' '),
6411 comment_suffix.starts_with(' '),
6412 );
6413
6414 if prefix_range.is_empty() || suffix_range.is_empty() {
6415 edits.push((
6416 prefix_range.start..prefix_range.start,
6417 full_comment_prefix.clone(),
6418 ));
6419 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6420 suffixes_inserted.push((end_row, comment_suffix.len()));
6421 } else {
6422 edits.push((prefix_range, empty_str.clone()));
6423 edits.push((suffix_range, empty_str.clone()));
6424 }
6425 } else {
6426 continue;
6427 }
6428 }
6429
6430 drop(snapshot);
6431 this.buffer.update(cx, |buffer, cx| {
6432 buffer.edit(edits, None, cx);
6433 });
6434
6435 // Adjust selections so that they end before any comment suffixes that
6436 // were inserted.
6437 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6438 let mut selections = this.selections.all::<Point>(cx);
6439 let snapshot = this.buffer.read(cx).read(cx);
6440 for selection in &mut selections {
6441 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6442 match row.cmp(&selection.end.row) {
6443 Ordering::Less => {
6444 suffixes_inserted.next();
6445 continue;
6446 }
6447 Ordering::Greater => break,
6448 Ordering::Equal => {
6449 if selection.end.column == snapshot.line_len(row) {
6450 if selection.is_empty() {
6451 selection.start.column -= suffix_len as u32;
6452 }
6453 selection.end.column -= suffix_len as u32;
6454 }
6455 break;
6456 }
6457 }
6458 }
6459 }
6460
6461 drop(snapshot);
6462 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6463
6464 let selections = this.selections.all::<Point>(cx);
6465 let selections_on_single_row = selections.windows(2).all(|selections| {
6466 selections[0].start.row == selections[1].start.row
6467 && selections[0].end.row == selections[1].end.row
6468 && selections[0].start.row == selections[0].end.row
6469 });
6470 let selections_selecting = selections
6471 .iter()
6472 .any(|selection| selection.start != selection.end);
6473 let advance_downwards = action.advance_downwards
6474 && selections_on_single_row
6475 && !selections_selecting
6476 && this.mode != EditorMode::SingleLine;
6477
6478 if advance_downwards {
6479 let snapshot = this.buffer.read(cx).snapshot(cx);
6480
6481 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6482 s.move_cursors_with(|display_snapshot, display_point, _| {
6483 let mut point = display_point.to_point(display_snapshot);
6484 point.row += 1;
6485 point = snapshot.clip_point(point, Bias::Left);
6486 let display_point = point.to_display_point(display_snapshot);
6487 (display_point, SelectionGoal::Column(display_point.column()))
6488 })
6489 });
6490 }
6491 });
6492 }
6493
6494 pub fn select_larger_syntax_node(
6495 &mut self,
6496 _: &SelectLargerSyntaxNode,
6497 cx: &mut ViewContext<Self>,
6498 ) {
6499 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6500 let buffer = self.buffer.read(cx).snapshot(cx);
6501 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6502
6503 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6504 let mut selected_larger_node = false;
6505 let new_selections = old_selections
6506 .iter()
6507 .map(|selection| {
6508 let old_range = selection.start..selection.end;
6509 let mut new_range = old_range.clone();
6510 while let Some(containing_range) =
6511 buffer.range_for_syntax_ancestor(new_range.clone())
6512 {
6513 new_range = containing_range;
6514 if !display_map.intersects_fold(new_range.start)
6515 && !display_map.intersects_fold(new_range.end)
6516 {
6517 break;
6518 }
6519 }
6520
6521 selected_larger_node |= new_range != old_range;
6522 Selection {
6523 id: selection.id,
6524 start: new_range.start,
6525 end: new_range.end,
6526 goal: SelectionGoal::None,
6527 reversed: selection.reversed,
6528 }
6529 })
6530 .collect::<Vec<_>>();
6531
6532 if selected_larger_node {
6533 stack.push(old_selections);
6534 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6535 s.select(new_selections);
6536 });
6537 }
6538 self.select_larger_syntax_node_stack = stack;
6539 }
6540
6541 pub fn select_smaller_syntax_node(
6542 &mut self,
6543 _: &SelectSmallerSyntaxNode,
6544 cx: &mut ViewContext<Self>,
6545 ) {
6546 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6547 if let Some(selections) = stack.pop() {
6548 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6549 s.select(selections.to_vec());
6550 });
6551 }
6552 self.select_larger_syntax_node_stack = stack;
6553 }
6554
6555 pub fn move_to_enclosing_bracket(
6556 &mut self,
6557 _: &MoveToEnclosingBracket,
6558 cx: &mut ViewContext<Self>,
6559 ) {
6560 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6561 s.move_offsets_with(|snapshot, selection| {
6562 let Some(enclosing_bracket_ranges) =
6563 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6564 else {
6565 return;
6566 };
6567
6568 let mut best_length = usize::MAX;
6569 let mut best_inside = false;
6570 let mut best_in_bracket_range = false;
6571 let mut best_destination = None;
6572 for (open, close) in enclosing_bracket_ranges {
6573 let close = close.to_inclusive();
6574 let length = close.end() - open.start;
6575 let inside = selection.start >= open.end && selection.end <= *close.start();
6576 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6577 || close.contains(&selection.head());
6578
6579 // If best is next to a bracket and current isn't, skip
6580 if !in_bracket_range && best_in_bracket_range {
6581 continue;
6582 }
6583
6584 // Prefer smaller lengths unless best is inside and current isn't
6585 if length > best_length && (best_inside || !inside) {
6586 continue;
6587 }
6588
6589 best_length = length;
6590 best_inside = inside;
6591 best_in_bracket_range = in_bracket_range;
6592 best_destination = Some(
6593 if close.contains(&selection.start) && close.contains(&selection.end) {
6594 if inside {
6595 open.end
6596 } else {
6597 open.start
6598 }
6599 } else {
6600 if inside {
6601 *close.start()
6602 } else {
6603 *close.end()
6604 }
6605 },
6606 );
6607 }
6608
6609 if let Some(destination) = best_destination {
6610 selection.collapse_to(destination, SelectionGoal::None);
6611 }
6612 })
6613 });
6614 }
6615
6616 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6617 self.end_selection(cx);
6618 self.selection_history.mode = SelectionHistoryMode::Undoing;
6619 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6620 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6621 self.select_next_state = entry.select_next_state;
6622 self.select_prev_state = entry.select_prev_state;
6623 self.add_selections_state = entry.add_selections_state;
6624 self.request_autoscroll(Autoscroll::newest(), cx);
6625 }
6626 self.selection_history.mode = SelectionHistoryMode::Normal;
6627 }
6628
6629 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6630 self.end_selection(cx);
6631 self.selection_history.mode = SelectionHistoryMode::Redoing;
6632 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6633 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6634 self.select_next_state = entry.select_next_state;
6635 self.select_prev_state = entry.select_prev_state;
6636 self.add_selections_state = entry.add_selections_state;
6637 self.request_autoscroll(Autoscroll::newest(), cx);
6638 }
6639 self.selection_history.mode = SelectionHistoryMode::Normal;
6640 }
6641
6642 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6643 self.go_to_diagnostic_impl(Direction::Next, cx)
6644 }
6645
6646 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6647 self.go_to_diagnostic_impl(Direction::Prev, cx)
6648 }
6649
6650 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6651 let buffer = self.buffer.read(cx).snapshot(cx);
6652 let selection = self.selections.newest::<usize>(cx);
6653
6654 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6655 if direction == Direction::Next {
6656 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6657 let (group_id, jump_to) = popover.activation_info();
6658 if self.activate_diagnostics(group_id, cx) {
6659 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6660 let mut new_selection = s.newest_anchor().clone();
6661 new_selection.collapse_to(jump_to, SelectionGoal::None);
6662 s.select_anchors(vec![new_selection.clone()]);
6663 });
6664 }
6665 return;
6666 }
6667 }
6668
6669 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6670 active_diagnostics
6671 .primary_range
6672 .to_offset(&buffer)
6673 .to_inclusive()
6674 });
6675 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6676 if active_primary_range.contains(&selection.head()) {
6677 *active_primary_range.end()
6678 } else {
6679 selection.head()
6680 }
6681 } else {
6682 selection.head()
6683 };
6684
6685 loop {
6686 let mut diagnostics = if direction == Direction::Prev {
6687 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6688 } else {
6689 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6690 };
6691 let group = diagnostics.find_map(|entry| {
6692 if entry.diagnostic.is_primary
6693 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6694 && !entry.range.is_empty()
6695 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6696 {
6697 Some((entry.range, entry.diagnostic.group_id))
6698 } else {
6699 None
6700 }
6701 });
6702
6703 if let Some((primary_range, group_id)) = group {
6704 if self.activate_diagnostics(group_id, cx) {
6705 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6706 s.select(vec![Selection {
6707 id: selection.id,
6708 start: primary_range.start,
6709 end: primary_range.start,
6710 reversed: false,
6711 goal: SelectionGoal::None,
6712 }]);
6713 });
6714 }
6715 break;
6716 } else {
6717 // Cycle around to the start of the buffer, potentially moving back to the start of
6718 // the currently active diagnostic.
6719 active_primary_range.take();
6720 if direction == Direction::Prev {
6721 if search_start == buffer.len() {
6722 break;
6723 } else {
6724 search_start = buffer.len();
6725 }
6726 } else if search_start == 0 {
6727 break;
6728 } else {
6729 search_start = 0;
6730 }
6731 }
6732 }
6733 }
6734
6735 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6736 let snapshot = self
6737 .display_map
6738 .update(cx, |display_map, cx| display_map.snapshot(cx));
6739 let selection = self.selections.newest::<Point>(cx);
6740
6741 if !self.seek_in_direction(
6742 &snapshot,
6743 selection.head(),
6744 false,
6745 snapshot
6746 .buffer_snapshot
6747 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6748 cx,
6749 ) {
6750 let wrapped_point = Point::zero();
6751 self.seek_in_direction(
6752 &snapshot,
6753 wrapped_point,
6754 true,
6755 snapshot
6756 .buffer_snapshot
6757 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6758 cx,
6759 );
6760 }
6761 }
6762
6763 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6764 let snapshot = self
6765 .display_map
6766 .update(cx, |display_map, cx| display_map.snapshot(cx));
6767 let selection = self.selections.newest::<Point>(cx);
6768
6769 if !self.seek_in_direction(
6770 &snapshot,
6771 selection.head(),
6772 false,
6773 snapshot
6774 .buffer_snapshot
6775 .git_diff_hunks_in_range_rev(0..selection.head().row),
6776 cx,
6777 ) {
6778 let wrapped_point = snapshot.buffer_snapshot.max_point();
6779 self.seek_in_direction(
6780 &snapshot,
6781 wrapped_point,
6782 true,
6783 snapshot
6784 .buffer_snapshot
6785 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6786 cx,
6787 );
6788 }
6789 }
6790
6791 fn seek_in_direction(
6792 &mut self,
6793 snapshot: &DisplaySnapshot,
6794 initial_point: Point,
6795 is_wrapped: bool,
6796 hunks: impl Iterator<Item = DiffHunk<u32>>,
6797 cx: &mut ViewContext<Editor>,
6798 ) -> bool {
6799 let display_point = initial_point.to_display_point(snapshot);
6800 let mut hunks = hunks
6801 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6802 .skip_while(|hunk| {
6803 if is_wrapped {
6804 false
6805 } else {
6806 hunk.contains_display_row(display_point.row())
6807 }
6808 })
6809 .dedup();
6810
6811 if let Some(hunk) = hunks.next() {
6812 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6813 let row = hunk.start_display_row();
6814 let point = DisplayPoint::new(row, 0);
6815 s.select_display_ranges([point..point]);
6816 });
6817
6818 true
6819 } else {
6820 false
6821 }
6822 }
6823
6824 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6825 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6826 }
6827
6828 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6829 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6830 }
6831
6832 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6833 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6834 }
6835
6836 pub fn go_to_type_definition_split(
6837 &mut self,
6838 _: &GoToTypeDefinitionSplit,
6839 cx: &mut ViewContext<Self>,
6840 ) {
6841 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6842 }
6843
6844 fn go_to_definition_of_kind(
6845 &mut self,
6846 kind: GotoDefinitionKind,
6847 split: bool,
6848 cx: &mut ViewContext<Self>,
6849 ) {
6850 let Some(workspace) = self.workspace(cx) else {
6851 return;
6852 };
6853 let buffer = self.buffer.read(cx);
6854 let head = self.selections.newest::<usize>(cx).head();
6855 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6856 text_anchor
6857 } else {
6858 return;
6859 };
6860
6861 let project = workspace.read(cx).project().clone();
6862 let definitions = project.update(cx, |project, cx| match kind {
6863 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6864 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6865 });
6866
6867 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6868 let definitions = definitions.await?;
6869 editor.update(&mut cx, |editor, cx| {
6870 editor.navigate_to_definitions(
6871 definitions
6872 .into_iter()
6873 .map(GoToDefinitionLink::Text)
6874 .collect(),
6875 split,
6876 cx,
6877 );
6878 })?;
6879 Ok::<(), anyhow::Error>(())
6880 })
6881 .detach_and_log_err(cx);
6882 }
6883
6884 pub fn navigate_to_definitions(
6885 &mut self,
6886 mut definitions: Vec<GoToDefinitionLink>,
6887 split: bool,
6888 cx: &mut ViewContext<Editor>,
6889 ) {
6890 let Some(workspace) = self.workspace(cx) else {
6891 return;
6892 };
6893 let pane = workspace.read(cx).active_pane().clone();
6894 // If there is one definition, just open it directly
6895 if definitions.len() == 1 {
6896 let definition = definitions.pop().unwrap();
6897 let target_task = match definition {
6898 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
6899 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6900 self.compute_target_location(lsp_location, server_id, cx)
6901 }
6902 };
6903 cx.spawn(|editor, mut cx| async move {
6904 let target = target_task.await.context("target resolution task")?;
6905 if let Some(target) = target {
6906 editor.update(&mut cx, |editor, cx| {
6907 let range = target.range.to_offset(target.buffer.read(cx));
6908 let range = editor.range_for_match(&range);
6909 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
6910 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6911 s.select_ranges([range]);
6912 });
6913 } else {
6914 cx.window_context().defer(move |cx| {
6915 let target_editor: ViewHandle<Self> =
6916 workspace.update(cx, |workspace, cx| {
6917 if split {
6918 workspace.split_project_item(target.buffer.clone(), cx)
6919 } else {
6920 workspace.open_project_item(target.buffer.clone(), cx)
6921 }
6922 });
6923 target_editor.update(cx, |target_editor, cx| {
6924 // When selecting a definition in a different buffer, disable the nav history
6925 // to avoid creating a history entry at the previous cursor location.
6926 pane.update(cx, |pane, _| pane.disable_history());
6927 target_editor.change_selections(
6928 Some(Autoscroll::fit()),
6929 cx,
6930 |s| {
6931 s.select_ranges([range]);
6932 },
6933 );
6934 pane.update(cx, |pane, _| pane.enable_history());
6935 });
6936 });
6937 }
6938 })
6939 } else {
6940 Ok(())
6941 }
6942 })
6943 .detach_and_log_err(cx);
6944 } else if !definitions.is_empty() {
6945 let replica_id = self.replica_id(cx);
6946 cx.spawn(|editor, mut cx| async move {
6947 let (title, location_tasks) = editor
6948 .update(&mut cx, |editor, cx| {
6949 let title = definitions
6950 .iter()
6951 .find_map(|definition| match definition {
6952 GoToDefinitionLink::Text(link) => {
6953 link.origin.as_ref().map(|origin| {
6954 let buffer = origin.buffer.read(cx);
6955 format!(
6956 "Definitions for {}",
6957 buffer
6958 .text_for_range(origin.range.clone())
6959 .collect::<String>()
6960 )
6961 })
6962 }
6963 GoToDefinitionLink::InlayHint(_, _) => None,
6964 })
6965 .unwrap_or("Definitions".to_string());
6966 let location_tasks = definitions
6967 .into_iter()
6968 .map(|definition| match definition {
6969 GoToDefinitionLink::Text(link) => {
6970 Task::Ready(Some(Ok(Some(link.target))))
6971 }
6972 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6973 editor.compute_target_location(lsp_location, server_id, cx)
6974 }
6975 })
6976 .collect::<Vec<_>>();
6977 (title, location_tasks)
6978 })
6979 .context("location tasks preparation")?;
6980
6981 let locations = futures::future::join_all(location_tasks)
6982 .await
6983 .into_iter()
6984 .filter_map(|location| location.transpose())
6985 .collect::<Result<_>>()
6986 .context("location tasks")?;
6987 workspace.update(&mut cx, |workspace, cx| {
6988 Self::open_locations_in_multibuffer(
6989 workspace, locations, replica_id, title, split, cx,
6990 )
6991 });
6992
6993 anyhow::Ok(())
6994 })
6995 .detach_and_log_err(cx);
6996 }
6997 }
6998
6999 fn compute_target_location(
7000 &self,
7001 lsp_location: lsp::Location,
7002 server_id: LanguageServerId,
7003 cx: &mut ViewContext<Editor>,
7004 ) -> Task<anyhow::Result<Option<Location>>> {
7005 let Some(project) = self.project.clone() else {
7006 return Task::Ready(Some(Ok(None)));
7007 };
7008
7009 cx.spawn(move |editor, mut cx| async move {
7010 let location_task = editor.update(&mut cx, |editor, cx| {
7011 project.update(cx, |project, cx| {
7012 let language_server_name =
7013 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
7014 project
7015 .language_server_for_buffer(buffer.read(cx), server_id, cx)
7016 .map(|(_, lsp_adapter)| {
7017 LanguageServerName(Arc::from(lsp_adapter.name()))
7018 })
7019 });
7020 language_server_name.map(|language_server_name| {
7021 project.open_local_buffer_via_lsp(
7022 lsp_location.uri.clone(),
7023 server_id,
7024 language_server_name,
7025 cx,
7026 )
7027 })
7028 })
7029 })?;
7030 let location = match location_task {
7031 Some(task) => Some({
7032 let target_buffer_handle = task.await.context("open local buffer")?;
7033 let range = {
7034 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7035 let target_start = target_buffer.clip_point_utf16(
7036 point_from_lsp(lsp_location.range.start),
7037 Bias::Left,
7038 );
7039 let target_end = target_buffer.clip_point_utf16(
7040 point_from_lsp(lsp_location.range.end),
7041 Bias::Left,
7042 );
7043 target_buffer.anchor_after(target_start)
7044 ..target_buffer.anchor_before(target_end)
7045 })
7046 };
7047 Location {
7048 buffer: target_buffer_handle,
7049 range,
7050 }
7051 }),
7052 None => None,
7053 };
7054 Ok(location)
7055 })
7056 }
7057
7058 pub fn find_all_references(
7059 workspace: &mut Workspace,
7060 _: &FindAllReferences,
7061 cx: &mut ViewContext<Workspace>,
7062 ) -> Option<Task<Result<()>>> {
7063 let active_item = workspace.active_item(cx)?;
7064 let editor_handle = active_item.act_as::<Self>(cx)?;
7065
7066 let editor = editor_handle.read(cx);
7067 let buffer = editor.buffer.read(cx);
7068 let head = editor.selections.newest::<usize>(cx).head();
7069 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7070 let replica_id = editor.replica_id(cx);
7071
7072 let project = workspace.project().clone();
7073 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7074 Some(cx.spawn_labeled(
7075 "Finding All References...",
7076 |workspace, mut cx| async move {
7077 let locations = references.await?;
7078 if locations.is_empty() {
7079 return Ok(());
7080 }
7081
7082 workspace.update(&mut cx, |workspace, cx| {
7083 let title = locations
7084 .first()
7085 .as_ref()
7086 .map(|location| {
7087 let buffer = location.buffer.read(cx);
7088 format!(
7089 "References to `{}`",
7090 buffer
7091 .text_for_range(location.range.clone())
7092 .collect::<String>()
7093 )
7094 })
7095 .unwrap();
7096 Self::open_locations_in_multibuffer(
7097 workspace, locations, replica_id, title, false, cx,
7098 );
7099 })?;
7100
7101 Ok(())
7102 },
7103 ))
7104 }
7105
7106 /// Opens a multibuffer with the given project locations in it
7107 pub fn open_locations_in_multibuffer(
7108 workspace: &mut Workspace,
7109 mut locations: Vec<Location>,
7110 replica_id: ReplicaId,
7111 title: String,
7112 split: bool,
7113 cx: &mut ViewContext<Workspace>,
7114 ) {
7115 // If there are multiple definitions, open them in a multibuffer
7116 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7117 let mut locations = locations.into_iter().peekable();
7118 let mut ranges_to_highlight = Vec::new();
7119
7120 let excerpt_buffer = cx.add_model(|cx| {
7121 let mut multibuffer = MultiBuffer::new(replica_id);
7122 while let Some(location) = locations.next() {
7123 let buffer = location.buffer.read(cx);
7124 let mut ranges_for_buffer = Vec::new();
7125 let range = location.range.to_offset(buffer);
7126 ranges_for_buffer.push(range.clone());
7127
7128 while let Some(next_location) = locations.peek() {
7129 if next_location.buffer == location.buffer {
7130 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7131 locations.next();
7132 } else {
7133 break;
7134 }
7135 }
7136
7137 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7138 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7139 location.buffer.clone(),
7140 ranges_for_buffer,
7141 1,
7142 cx,
7143 ))
7144 }
7145
7146 multibuffer.with_title(title)
7147 });
7148
7149 let editor = cx.add_view(|cx| {
7150 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7151 });
7152 editor.update(cx, |editor, cx| {
7153 editor.highlight_background::<Self>(
7154 ranges_to_highlight,
7155 |theme| theme.editor.highlighted_line_background,
7156 cx,
7157 );
7158 });
7159 if split {
7160 workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
7161 } else {
7162 workspace.add_item(Box::new(editor), cx);
7163 }
7164 }
7165
7166 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7167 use language::ToOffset as _;
7168
7169 let project = self.project.clone()?;
7170 let selection = self.selections.newest_anchor().clone();
7171 let (cursor_buffer, cursor_buffer_position) = self
7172 .buffer
7173 .read(cx)
7174 .text_anchor_for_position(selection.head(), cx)?;
7175 let (tail_buffer, _) = self
7176 .buffer
7177 .read(cx)
7178 .text_anchor_for_position(selection.tail(), cx)?;
7179 if tail_buffer != cursor_buffer {
7180 return None;
7181 }
7182
7183 let snapshot = cursor_buffer.read(cx).snapshot();
7184 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7185 let prepare_rename = project.update(cx, |project, cx| {
7186 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7187 });
7188
7189 Some(cx.spawn(|this, mut cx| async move {
7190 let rename_range = if let Some(range) = prepare_rename.await? {
7191 Some(range)
7192 } else {
7193 this.update(&mut cx, |this, cx| {
7194 let buffer = this.buffer.read(cx).snapshot(cx);
7195 let mut buffer_highlights = this
7196 .document_highlights_for_position(selection.head(), &buffer)
7197 .filter(|highlight| {
7198 highlight.start.excerpt_id() == selection.head().excerpt_id()
7199 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7200 });
7201 buffer_highlights
7202 .next()
7203 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7204 })?
7205 };
7206 if let Some(rename_range) = rename_range {
7207 let rename_buffer_range = rename_range.to_offset(&snapshot);
7208 let cursor_offset_in_rename_range =
7209 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7210
7211 this.update(&mut cx, |this, cx| {
7212 this.take_rename(false, cx);
7213 let style = this.style(cx);
7214 let buffer = this.buffer.read(cx).read(cx);
7215 let cursor_offset = selection.head().to_offset(&buffer);
7216 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7217 let rename_end = rename_start + rename_buffer_range.len();
7218 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7219 let mut old_highlight_id = None;
7220 let old_name: Arc<str> = buffer
7221 .chunks(rename_start..rename_end, true)
7222 .map(|chunk| {
7223 if old_highlight_id.is_none() {
7224 old_highlight_id = chunk.syntax_highlight_id;
7225 }
7226 chunk.text
7227 })
7228 .collect::<String>()
7229 .into();
7230
7231 drop(buffer);
7232
7233 // Position the selection in the rename editor so that it matches the current selection.
7234 this.show_local_selections = false;
7235 let rename_editor = cx.add_view(|cx| {
7236 let mut editor = Editor::single_line(None, cx);
7237 if let Some(old_highlight_id) = old_highlight_id {
7238 editor.override_text_style =
7239 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7240 }
7241 editor.buffer.update(cx, |buffer, cx| {
7242 buffer.edit([(0..0, old_name.clone())], None, cx)
7243 });
7244 editor.select_all(&SelectAll, cx);
7245 editor
7246 });
7247
7248 let ranges = this
7249 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7250 .into_iter()
7251 .flat_map(|(_, ranges)| ranges.into_iter())
7252 .chain(
7253 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7254 .into_iter()
7255 .flat_map(|(_, ranges)| ranges.into_iter()),
7256 )
7257 .collect();
7258
7259 this.highlight_text::<Rename>(
7260 ranges,
7261 HighlightStyle {
7262 fade_out: Some(style.rename_fade),
7263 ..Default::default()
7264 },
7265 cx,
7266 );
7267 cx.focus(&rename_editor);
7268 let block_id = this.insert_blocks(
7269 [BlockProperties {
7270 style: BlockStyle::Flex,
7271 position: range.start.clone(),
7272 height: 1,
7273 render: Arc::new({
7274 let editor = rename_editor.clone();
7275 move |cx: &mut BlockContext| {
7276 ChildView::new(&editor, cx)
7277 .contained()
7278 .with_padding_left(cx.anchor_x)
7279 .into_any()
7280 }
7281 }),
7282 disposition: BlockDisposition::Below,
7283 }],
7284 Some(Autoscroll::fit()),
7285 cx,
7286 )[0];
7287 this.pending_rename = Some(RenameState {
7288 range,
7289 old_name,
7290 editor: rename_editor,
7291 block_id,
7292 });
7293 })?;
7294 }
7295
7296 Ok(())
7297 }))
7298 }
7299
7300 pub fn confirm_rename(
7301 workspace: &mut Workspace,
7302 _: &ConfirmRename,
7303 cx: &mut ViewContext<Workspace>,
7304 ) -> Option<Task<Result<()>>> {
7305 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7306
7307 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7308 let rename = editor.take_rename(false, cx)?;
7309 let buffer = editor.buffer.read(cx);
7310 let (start_buffer, start) =
7311 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7312 let (end_buffer, end) =
7313 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7314 if start_buffer == end_buffer {
7315 let new_name = rename.editor.read(cx).text(cx);
7316 Some((start_buffer, start..end, rename.old_name, new_name))
7317 } else {
7318 None
7319 }
7320 })?;
7321
7322 let rename = workspace.project().clone().update(cx, |project, cx| {
7323 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7324 });
7325
7326 let editor = editor.downgrade();
7327 Some(cx.spawn(|workspace, mut cx| async move {
7328 let project_transaction = rename.await?;
7329 Self::open_project_transaction(
7330 &editor,
7331 workspace,
7332 project_transaction,
7333 format!("Rename: {} → {}", old_name, new_name),
7334 cx.clone(),
7335 )
7336 .await?;
7337
7338 editor.update(&mut cx, |editor, cx| {
7339 editor.refresh_document_highlights(cx);
7340 })?;
7341 Ok(())
7342 }))
7343 }
7344
7345 fn take_rename(
7346 &mut self,
7347 moving_cursor: bool,
7348 cx: &mut ViewContext<Self>,
7349 ) -> Option<RenameState> {
7350 let rename = self.pending_rename.take()?;
7351 self.remove_blocks(
7352 [rename.block_id].into_iter().collect(),
7353 Some(Autoscroll::fit()),
7354 cx,
7355 );
7356 self.clear_highlights::<Rename>(cx);
7357 self.show_local_selections = true;
7358
7359 if moving_cursor {
7360 let rename_editor = rename.editor.read(cx);
7361 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7362
7363 // Update the selection to match the position of the selection inside
7364 // the rename editor.
7365 let snapshot = self.buffer.read(cx).read(cx);
7366 let rename_range = rename.range.to_offset(&snapshot);
7367 let cursor_in_editor = snapshot
7368 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7369 .min(rename_range.end);
7370 drop(snapshot);
7371
7372 self.change_selections(None, cx, |s| {
7373 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7374 });
7375 } else {
7376 self.refresh_document_highlights(cx);
7377 }
7378
7379 Some(rename)
7380 }
7381
7382 #[cfg(any(test, feature = "test-support"))]
7383 pub fn pending_rename(&self) -> Option<&RenameState> {
7384 self.pending_rename.as_ref()
7385 }
7386
7387 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7388 let project = match &self.project {
7389 Some(project) => project.clone(),
7390 None => return None,
7391 };
7392
7393 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7394 }
7395
7396 fn perform_format(
7397 &mut self,
7398 project: ModelHandle<Project>,
7399 trigger: FormatTrigger,
7400 cx: &mut ViewContext<Self>,
7401 ) -> Task<Result<()>> {
7402 let buffer = self.buffer().clone();
7403 let buffers = buffer.read(cx).all_buffers();
7404
7405 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7406 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7407
7408 cx.spawn(|_, mut cx| async move {
7409 let transaction = futures::select_biased! {
7410 _ = timeout => {
7411 log::warn!("timed out waiting for formatting");
7412 None
7413 }
7414 transaction = format.log_err().fuse() => transaction,
7415 };
7416
7417 buffer.update(&mut cx, |buffer, cx| {
7418 if let Some(transaction) = transaction {
7419 if !buffer.is_singleton() {
7420 buffer.push_transaction(&transaction.0, cx);
7421 }
7422 }
7423
7424 cx.notify();
7425 });
7426
7427 Ok(())
7428 })
7429 }
7430
7431 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7432 if let Some(project) = self.project.clone() {
7433 self.buffer.update(cx, |multi_buffer, cx| {
7434 project.update(cx, |project, cx| {
7435 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7436 });
7437 })
7438 }
7439 }
7440
7441 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7442 cx.show_character_palette();
7443 }
7444
7445 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7446 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7447 let buffer = self.buffer.read(cx).snapshot(cx);
7448 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7449 let is_valid = buffer
7450 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7451 .any(|entry| {
7452 entry.diagnostic.is_primary
7453 && !entry.range.is_empty()
7454 && entry.range.start == primary_range_start
7455 && entry.diagnostic.message == active_diagnostics.primary_message
7456 });
7457
7458 if is_valid != active_diagnostics.is_valid {
7459 active_diagnostics.is_valid = is_valid;
7460 let mut new_styles = HashMap::default();
7461 for (block_id, diagnostic) in &active_diagnostics.blocks {
7462 new_styles.insert(
7463 *block_id,
7464 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7465 );
7466 }
7467 self.display_map
7468 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7469 }
7470 }
7471 }
7472
7473 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7474 self.dismiss_diagnostics(cx);
7475 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7476 let buffer = self.buffer.read(cx).snapshot(cx);
7477
7478 let mut primary_range = None;
7479 let mut primary_message = None;
7480 let mut group_end = Point::zero();
7481 let diagnostic_group = buffer
7482 .diagnostic_group::<Point>(group_id)
7483 .map(|entry| {
7484 if entry.range.end > group_end {
7485 group_end = entry.range.end;
7486 }
7487 if entry.diagnostic.is_primary {
7488 primary_range = Some(entry.range.clone());
7489 primary_message = Some(entry.diagnostic.message.clone());
7490 }
7491 entry
7492 })
7493 .collect::<Vec<_>>();
7494 let primary_range = primary_range?;
7495 let primary_message = primary_message?;
7496 let primary_range =
7497 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7498
7499 let blocks = display_map
7500 .insert_blocks(
7501 diagnostic_group.iter().map(|entry| {
7502 let diagnostic = entry.diagnostic.clone();
7503 let message_height = diagnostic.message.lines().count() as u8;
7504 BlockProperties {
7505 style: BlockStyle::Fixed,
7506 position: buffer.anchor_after(entry.range.start),
7507 height: message_height,
7508 render: diagnostic_block_renderer(diagnostic, true),
7509 disposition: BlockDisposition::Below,
7510 }
7511 }),
7512 cx,
7513 )
7514 .into_iter()
7515 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7516 .collect();
7517
7518 Some(ActiveDiagnosticGroup {
7519 primary_range,
7520 primary_message,
7521 blocks,
7522 is_valid: true,
7523 })
7524 });
7525 self.active_diagnostics.is_some()
7526 }
7527
7528 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7529 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7530 self.display_map.update(cx, |display_map, cx| {
7531 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7532 });
7533 cx.notify();
7534 }
7535 }
7536
7537 pub fn set_selections_from_remote(
7538 &mut self,
7539 selections: Vec<Selection<Anchor>>,
7540 pending_selection: Option<Selection<Anchor>>,
7541 cx: &mut ViewContext<Self>,
7542 ) {
7543 let old_cursor_position = self.selections.newest_anchor().head();
7544 self.selections.change_with(cx, |s| {
7545 s.select_anchors(selections);
7546 if let Some(pending_selection) = pending_selection {
7547 s.set_pending(pending_selection, SelectMode::Character);
7548 } else {
7549 s.clear_pending();
7550 }
7551 });
7552 self.selections_did_change(false, &old_cursor_position, cx);
7553 }
7554
7555 fn push_to_selection_history(&mut self) {
7556 self.selection_history.push(SelectionHistoryEntry {
7557 selections: self.selections.disjoint_anchors(),
7558 select_next_state: self.select_next_state.clone(),
7559 select_prev_state: self.select_prev_state.clone(),
7560 add_selections_state: self.add_selections_state.clone(),
7561 });
7562 }
7563
7564 pub fn transact(
7565 &mut self,
7566 cx: &mut ViewContext<Self>,
7567 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7568 ) -> Option<TransactionId> {
7569 self.start_transaction_at(Instant::now(), cx);
7570 update(self, cx);
7571 self.end_transaction_at(Instant::now(), cx)
7572 }
7573
7574 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7575 self.end_selection(cx);
7576 if let Some(tx_id) = self
7577 .buffer
7578 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7579 {
7580 self.selection_history
7581 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7582 }
7583 }
7584
7585 fn end_transaction_at(
7586 &mut self,
7587 now: Instant,
7588 cx: &mut ViewContext<Self>,
7589 ) -> Option<TransactionId> {
7590 if let Some(tx_id) = self
7591 .buffer
7592 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7593 {
7594 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7595 *end_selections = Some(self.selections.disjoint_anchors());
7596 } else {
7597 error!("unexpectedly ended a transaction that wasn't started by this editor");
7598 }
7599
7600 cx.emit(Event::Edited);
7601 Some(tx_id)
7602 } else {
7603 None
7604 }
7605 }
7606
7607 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7608 let mut fold_ranges = Vec::new();
7609
7610 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7611
7612 let selections = self.selections.all_adjusted(cx);
7613 for selection in selections {
7614 let range = selection.range().sorted();
7615 let buffer_start_row = range.start.row;
7616
7617 for row in (0..=range.end.row).rev() {
7618 let fold_range = display_map.foldable_range(row);
7619
7620 if let Some(fold_range) = fold_range {
7621 if fold_range.end.row >= buffer_start_row {
7622 fold_ranges.push(fold_range);
7623 if row <= range.start.row {
7624 break;
7625 }
7626 }
7627 }
7628 }
7629 }
7630
7631 self.fold_ranges(fold_ranges, true, cx);
7632 }
7633
7634 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7635 let buffer_row = fold_at.buffer_row;
7636 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7637
7638 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7639 let autoscroll = self
7640 .selections
7641 .all::<Point>(cx)
7642 .iter()
7643 .any(|selection| fold_range.overlaps(&selection.range()));
7644
7645 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7646 }
7647 }
7648
7649 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7650 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7651 let buffer = &display_map.buffer_snapshot;
7652 let selections = self.selections.all::<Point>(cx);
7653 let ranges = selections
7654 .iter()
7655 .map(|s| {
7656 let range = s.display_range(&display_map).sorted();
7657 let mut start = range.start.to_point(&display_map);
7658 let mut end = range.end.to_point(&display_map);
7659 start.column = 0;
7660 end.column = buffer.line_len(end.row);
7661 start..end
7662 })
7663 .collect::<Vec<_>>();
7664
7665 self.unfold_ranges(ranges, true, true, cx);
7666 }
7667
7668 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7669 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7670
7671 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7672 ..Point::new(
7673 unfold_at.buffer_row,
7674 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7675 );
7676
7677 let autoscroll = self
7678 .selections
7679 .all::<Point>(cx)
7680 .iter()
7681 .any(|selection| selection.range().overlaps(&intersection_range));
7682
7683 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7684 }
7685
7686 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7687 let selections = self.selections.all::<Point>(cx);
7688 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7689 let line_mode = self.selections.line_mode;
7690 let ranges = selections.into_iter().map(|s| {
7691 if line_mode {
7692 let start = Point::new(s.start.row, 0);
7693 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
7694 start..end
7695 } else {
7696 s.start..s.end
7697 }
7698 });
7699 self.fold_ranges(ranges, true, cx);
7700 }
7701
7702 pub fn fold_ranges<T: ToOffset + Clone>(
7703 &mut self,
7704 ranges: impl IntoIterator<Item = Range<T>>,
7705 auto_scroll: bool,
7706 cx: &mut ViewContext<Self>,
7707 ) {
7708 let mut ranges = ranges.into_iter().peekable();
7709 if ranges.peek().is_some() {
7710 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7711
7712 if auto_scroll {
7713 self.request_autoscroll(Autoscroll::fit(), cx);
7714 }
7715
7716 cx.notify();
7717 }
7718 }
7719
7720 pub fn unfold_ranges<T: ToOffset + Clone>(
7721 &mut self,
7722 ranges: impl IntoIterator<Item = Range<T>>,
7723 inclusive: bool,
7724 auto_scroll: bool,
7725 cx: &mut ViewContext<Self>,
7726 ) {
7727 let mut ranges = ranges.into_iter().peekable();
7728 if ranges.peek().is_some() {
7729 self.display_map
7730 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7731 if auto_scroll {
7732 self.request_autoscroll(Autoscroll::fit(), cx);
7733 }
7734
7735 cx.notify();
7736 }
7737 }
7738
7739 pub fn gutter_hover(
7740 &mut self,
7741 GutterHover { hovered }: &GutterHover,
7742 cx: &mut ViewContext<Self>,
7743 ) {
7744 self.gutter_hovered = *hovered;
7745 cx.notify();
7746 }
7747
7748 pub fn insert_blocks(
7749 &mut self,
7750 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7751 autoscroll: Option<Autoscroll>,
7752 cx: &mut ViewContext<Self>,
7753 ) -> Vec<BlockId> {
7754 let blocks = self
7755 .display_map
7756 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7757 if let Some(autoscroll) = autoscroll {
7758 self.request_autoscroll(autoscroll, cx);
7759 }
7760 blocks
7761 }
7762
7763 pub fn replace_blocks(
7764 &mut self,
7765 blocks: HashMap<BlockId, RenderBlock>,
7766 autoscroll: Option<Autoscroll>,
7767 cx: &mut ViewContext<Self>,
7768 ) {
7769 self.display_map
7770 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7771 if let Some(autoscroll) = autoscroll {
7772 self.request_autoscroll(autoscroll, cx);
7773 }
7774 }
7775
7776 pub fn remove_blocks(
7777 &mut self,
7778 block_ids: HashSet<BlockId>,
7779 autoscroll: Option<Autoscroll>,
7780 cx: &mut ViewContext<Self>,
7781 ) {
7782 self.display_map.update(cx, |display_map, cx| {
7783 display_map.remove_blocks(block_ids, cx)
7784 });
7785 if let Some(autoscroll) = autoscroll {
7786 self.request_autoscroll(autoscroll, cx);
7787 }
7788 }
7789
7790 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7791 self.display_map
7792 .update(cx, |map, cx| map.snapshot(cx))
7793 .longest_row()
7794 }
7795
7796 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7797 self.display_map
7798 .update(cx, |map, cx| map.snapshot(cx))
7799 .max_point()
7800 }
7801
7802 pub fn text(&self, cx: &AppContext) -> String {
7803 self.buffer.read(cx).read(cx).text()
7804 }
7805
7806 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7807 self.transact(cx, |this, cx| {
7808 this.buffer
7809 .read(cx)
7810 .as_singleton()
7811 .expect("you can only call set_text on editors for singleton buffers")
7812 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7813 });
7814 }
7815
7816 pub fn display_text(&self, cx: &mut AppContext) -> String {
7817 self.display_map
7818 .update(cx, |map, cx| map.snapshot(cx))
7819 .text()
7820 }
7821
7822 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7823 let mut wrap_guides = smallvec::smallvec![];
7824
7825 if self.show_wrap_guides == Some(false) {
7826 return wrap_guides;
7827 }
7828
7829 let settings = self.buffer.read(cx).settings_at(0, cx);
7830 if settings.show_wrap_guides {
7831 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7832 wrap_guides.push((soft_wrap as usize, true));
7833 }
7834 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7835 }
7836
7837 wrap_guides
7838 }
7839
7840 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7841 let settings = self.buffer.read(cx).settings_at(0, cx);
7842 let mode = self
7843 .soft_wrap_mode_override
7844 .unwrap_or_else(|| settings.soft_wrap);
7845 match mode {
7846 language_settings::SoftWrap::None => SoftWrap::None,
7847 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7848 language_settings::SoftWrap::PreferredLineLength => {
7849 SoftWrap::Column(settings.preferred_line_length)
7850 }
7851 }
7852 }
7853
7854 pub fn set_soft_wrap_mode(
7855 &mut self,
7856 mode: language_settings::SoftWrap,
7857 cx: &mut ViewContext<Self>,
7858 ) {
7859 self.soft_wrap_mode_override = Some(mode);
7860 cx.notify();
7861 }
7862
7863 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7864 self.display_map
7865 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7866 }
7867
7868 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7869 if self.soft_wrap_mode_override.is_some() {
7870 self.soft_wrap_mode_override.take();
7871 } else {
7872 let soft_wrap = match self.soft_wrap_mode(cx) {
7873 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7874 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7875 };
7876 self.soft_wrap_mode_override = Some(soft_wrap);
7877 }
7878 cx.notify();
7879 }
7880
7881 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7882 self.show_gutter = show_gutter;
7883 cx.notify();
7884 }
7885
7886 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7887 self.show_wrap_guides = Some(show_gutter);
7888 cx.notify();
7889 }
7890
7891 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7892 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7893 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7894 cx.reveal_path(&file.abs_path(cx));
7895 }
7896 }
7897 }
7898
7899 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7900 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7901 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7902 if let Some(path) = file.abs_path(cx).to_str() {
7903 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7904 }
7905 }
7906 }
7907 }
7908
7909 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7910 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7911 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7912 if let Some(path) = file.path().to_str() {
7913 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7914 }
7915 }
7916 }
7917 }
7918
7919 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7920 self.highlighted_rows = rows;
7921 }
7922
7923 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7924 self.highlighted_rows.clone()
7925 }
7926
7927 pub fn highlight_background<T: 'static>(
7928 &mut self,
7929 ranges: Vec<Range<Anchor>>,
7930 color_fetcher: fn(&Theme) -> Color,
7931 cx: &mut ViewContext<Self>,
7932 ) {
7933 self.background_highlights
7934 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7935 cx.notify();
7936 }
7937
7938 pub fn highlight_inlay_background<T: 'static>(
7939 &mut self,
7940 ranges: Vec<InlayHighlight>,
7941 color_fetcher: fn(&Theme) -> Color,
7942 cx: &mut ViewContext<Self>,
7943 ) {
7944 // TODO: no actual highlights happen for inlays currently, find a way to do that
7945 self.inlay_background_highlights
7946 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
7947 cx.notify();
7948 }
7949
7950 pub fn clear_background_highlights<T: 'static>(
7951 &mut self,
7952 cx: &mut ViewContext<Self>,
7953 ) -> Option<BackgroundHighlight> {
7954 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
7955 let inlay_highlights = self
7956 .inlay_background_highlights
7957 .remove(&Some(TypeId::of::<T>()));
7958 if text_highlights.is_some() || inlay_highlights.is_some() {
7959 cx.notify();
7960 }
7961 text_highlights
7962 }
7963
7964 #[cfg(feature = "test-support")]
7965 pub fn all_text_background_highlights(
7966 &mut self,
7967 cx: &mut ViewContext<Self>,
7968 ) -> Vec<(Range<DisplayPoint>, Color)> {
7969 let snapshot = self.snapshot(cx);
7970 let buffer = &snapshot.buffer_snapshot;
7971 let start = buffer.anchor_before(0);
7972 let end = buffer.anchor_after(buffer.len());
7973 let theme = theme::current(cx);
7974 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7975 }
7976
7977 fn document_highlights_for_position<'a>(
7978 &'a self,
7979 position: Anchor,
7980 buffer: &'a MultiBufferSnapshot,
7981 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7982 let read_highlights = self
7983 .background_highlights
7984 .get(&TypeId::of::<DocumentHighlightRead>())
7985 .map(|h| &h.1);
7986 let write_highlights = self
7987 .background_highlights
7988 .get(&TypeId::of::<DocumentHighlightWrite>())
7989 .map(|h| &h.1);
7990 let left_position = position.bias_left(buffer);
7991 let right_position = position.bias_right(buffer);
7992 read_highlights
7993 .into_iter()
7994 .chain(write_highlights)
7995 .flat_map(move |ranges| {
7996 let start_ix = match ranges.binary_search_by(|probe| {
7997 let cmp = probe.end.cmp(&left_position, buffer);
7998 if cmp.is_ge() {
7999 Ordering::Greater
8000 } else {
8001 Ordering::Less
8002 }
8003 }) {
8004 Ok(i) | Err(i) => i,
8005 };
8006
8007 let right_position = right_position.clone();
8008 ranges[start_ix..]
8009 .iter()
8010 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
8011 })
8012 }
8013
8014 pub fn background_highlights_in_range(
8015 &self,
8016 search_range: Range<Anchor>,
8017 display_snapshot: &DisplaySnapshot,
8018 theme: &Theme,
8019 ) -> Vec<(Range<DisplayPoint>, Color)> {
8020 let mut results = Vec::new();
8021 for (color_fetcher, ranges) in self.background_highlights.values() {
8022 let color = color_fetcher(theme);
8023 let start_ix = match ranges.binary_search_by(|probe| {
8024 let cmp = probe
8025 .end
8026 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8027 if cmp.is_gt() {
8028 Ordering::Greater
8029 } else {
8030 Ordering::Less
8031 }
8032 }) {
8033 Ok(i) | Err(i) => i,
8034 };
8035 for range in &ranges[start_ix..] {
8036 if range
8037 .start
8038 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8039 .is_ge()
8040 {
8041 break;
8042 }
8043
8044 let start = range.start.to_display_point(&display_snapshot);
8045 let end = range.end.to_display_point(&display_snapshot);
8046 results.push((start..end, color))
8047 }
8048 }
8049 results
8050 }
8051
8052 pub fn background_highlight_row_ranges<T: 'static>(
8053 &self,
8054 search_range: Range<Anchor>,
8055 display_snapshot: &DisplaySnapshot,
8056 count: usize,
8057 ) -> Vec<RangeInclusive<DisplayPoint>> {
8058 let mut results = Vec::new();
8059 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8060 return vec![];
8061 };
8062
8063 let start_ix = match ranges.binary_search_by(|probe| {
8064 let cmp = probe
8065 .end
8066 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8067 if cmp.is_gt() {
8068 Ordering::Greater
8069 } else {
8070 Ordering::Less
8071 }
8072 }) {
8073 Ok(i) | Err(i) => i,
8074 };
8075 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8076 if let (Some(start_display), Some(end_display)) = (start, end) {
8077 results.push(
8078 start_display.to_display_point(display_snapshot)
8079 ..=end_display.to_display_point(display_snapshot),
8080 );
8081 }
8082 };
8083 let mut start_row: Option<Point> = None;
8084 let mut end_row: Option<Point> = None;
8085 if ranges.len() > count {
8086 return Vec::new();
8087 }
8088 for range in &ranges[start_ix..] {
8089 if range
8090 .start
8091 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8092 .is_ge()
8093 {
8094 break;
8095 }
8096 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8097 if let Some(current_row) = &end_row {
8098 if end.row == current_row.row {
8099 continue;
8100 }
8101 }
8102 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8103 if start_row.is_none() {
8104 assert_eq!(end_row, None);
8105 start_row = Some(start);
8106 end_row = Some(end);
8107 continue;
8108 }
8109 if let Some(current_end) = end_row.as_mut() {
8110 if start.row > current_end.row + 1 {
8111 push_region(start_row, end_row);
8112 start_row = Some(start);
8113 end_row = Some(end);
8114 } else {
8115 // Merge two hunks.
8116 *current_end = end;
8117 }
8118 } else {
8119 unreachable!();
8120 }
8121 }
8122 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8123 push_region(start_row, end_row);
8124 results
8125 }
8126
8127 pub fn highlight_text<T: 'static>(
8128 &mut self,
8129 ranges: Vec<Range<Anchor>>,
8130 style: HighlightStyle,
8131 cx: &mut ViewContext<Self>,
8132 ) {
8133 self.display_map.update(cx, |map, _| {
8134 map.highlight_text(TypeId::of::<T>(), ranges, style)
8135 });
8136 cx.notify();
8137 }
8138
8139 pub fn highlight_inlays<T: 'static>(
8140 &mut self,
8141 highlights: Vec<InlayHighlight>,
8142 style: HighlightStyle,
8143 cx: &mut ViewContext<Self>,
8144 ) {
8145 self.display_map.update(cx, |map, _| {
8146 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8147 });
8148 cx.notify();
8149 }
8150
8151 pub fn text_highlights<'a, T: 'static>(
8152 &'a self,
8153 cx: &'a AppContext,
8154 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8155 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8156 }
8157
8158 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8159 let cleared = self
8160 .display_map
8161 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8162 if cleared {
8163 cx.notify();
8164 }
8165 }
8166
8167 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8168 self.blink_manager.read(cx).visible() && self.focused
8169 }
8170
8171 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8172 cx.notify();
8173 }
8174
8175 fn on_buffer_event(
8176 &mut self,
8177 multibuffer: ModelHandle<MultiBuffer>,
8178 event: &multi_buffer::Event,
8179 cx: &mut ViewContext<Self>,
8180 ) {
8181 match event {
8182 multi_buffer::Event::Edited {
8183 sigleton_buffer_edited,
8184 } => {
8185 self.refresh_active_diagnostics(cx);
8186 self.refresh_code_actions(cx);
8187 if self.has_active_copilot_suggestion(cx) {
8188 self.update_visible_copilot_suggestion(cx);
8189 }
8190 cx.emit(Event::BufferEdited);
8191
8192 if *sigleton_buffer_edited {
8193 if let Some(project) = &self.project {
8194 let project = project.read(cx);
8195 let languages_affected = multibuffer
8196 .read(cx)
8197 .all_buffers()
8198 .into_iter()
8199 .filter_map(|buffer| {
8200 let buffer = buffer.read(cx);
8201 let language = buffer.language()?;
8202 if project.is_local()
8203 && project.language_servers_for_buffer(buffer, cx).count() == 0
8204 {
8205 None
8206 } else {
8207 Some(language)
8208 }
8209 })
8210 .cloned()
8211 .collect::<HashSet<_>>();
8212 if !languages_affected.is_empty() {
8213 self.refresh_inlay_hints(
8214 InlayHintRefreshReason::BufferEdited(languages_affected),
8215 cx,
8216 );
8217 }
8218 }
8219 }
8220 }
8221 multi_buffer::Event::ExcerptsAdded {
8222 buffer,
8223 predecessor,
8224 excerpts,
8225 } => {
8226 cx.emit(Event::ExcerptsAdded {
8227 buffer: buffer.clone(),
8228 predecessor: *predecessor,
8229 excerpts: excerpts.clone(),
8230 });
8231 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8232 }
8233 multi_buffer::Event::ExcerptsRemoved { ids } => {
8234 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8235 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8236 }
8237 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8238 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8239 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8240 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8241 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8242 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8243 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8244 multi_buffer::Event::DiagnosticsUpdated => {
8245 self.refresh_active_diagnostics(cx);
8246 }
8247 _ => {}
8248 };
8249 }
8250
8251 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8252 cx.notify();
8253 }
8254
8255 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8256 self.refresh_copilot_suggestions(true, cx);
8257 self.refresh_inlay_hints(
8258 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8259 self.selections.newest_anchor().head(),
8260 &self.buffer.read(cx).snapshot(cx),
8261 cx,
8262 )),
8263 cx,
8264 );
8265 }
8266
8267 pub fn set_searchable(&mut self, searchable: bool) {
8268 self.searchable = searchable;
8269 }
8270
8271 pub fn searchable(&self) -> bool {
8272 self.searchable
8273 }
8274
8275 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8276 let active_item = workspace.active_item(cx);
8277 let editor_handle = if let Some(editor) = active_item
8278 .as_ref()
8279 .and_then(|item| item.act_as::<Self>(cx))
8280 {
8281 editor
8282 } else {
8283 cx.propagate_action();
8284 return;
8285 };
8286
8287 let editor = editor_handle.read(cx);
8288 let buffer = editor.buffer.read(cx);
8289 if buffer.is_singleton() {
8290 cx.propagate_action();
8291 return;
8292 }
8293
8294 let mut new_selections_by_buffer = HashMap::default();
8295 for selection in editor.selections.all::<usize>(cx) {
8296 for (buffer, mut range, _) in
8297 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8298 {
8299 if selection.reversed {
8300 mem::swap(&mut range.start, &mut range.end);
8301 }
8302 new_selections_by_buffer
8303 .entry(buffer)
8304 .or_insert(Vec::new())
8305 .push(range)
8306 }
8307 }
8308
8309 editor_handle.update(cx, |editor, cx| {
8310 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8311 });
8312 let pane = workspace.active_pane().clone();
8313 pane.update(cx, |pane, _| pane.disable_history());
8314
8315 // We defer the pane interaction because we ourselves are a workspace item
8316 // and activating a new item causes the pane to call a method on us reentrantly,
8317 // which panics if we're on the stack.
8318 cx.defer(move |workspace, cx| {
8319 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8320 let editor = workspace.open_project_item::<Self>(buffer, cx);
8321 editor.update(cx, |editor, cx| {
8322 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8323 s.select_ranges(ranges);
8324 });
8325 });
8326 }
8327
8328 pane.update(cx, |pane, _| pane.enable_history());
8329 });
8330 }
8331
8332 fn jump(
8333 workspace: &mut Workspace,
8334 path: ProjectPath,
8335 position: Point,
8336 anchor: language::Anchor,
8337 cx: &mut ViewContext<Workspace>,
8338 ) {
8339 let editor = workspace.open_path(path, None, true, cx);
8340 cx.spawn(|_, mut cx| async move {
8341 let editor = editor
8342 .await?
8343 .downcast::<Editor>()
8344 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8345 .downgrade();
8346 editor.update(&mut cx, |editor, cx| {
8347 let buffer = editor
8348 .buffer()
8349 .read(cx)
8350 .as_singleton()
8351 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8352 let buffer = buffer.read(cx);
8353 let cursor = if buffer.can_resolve(&anchor) {
8354 language::ToPoint::to_point(&anchor, buffer)
8355 } else {
8356 buffer.clip_point(position, Bias::Left)
8357 };
8358
8359 let nav_history = editor.nav_history.take();
8360 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8361 s.select_ranges([cursor..cursor]);
8362 });
8363 editor.nav_history = nav_history;
8364
8365 anyhow::Ok(())
8366 })??;
8367
8368 anyhow::Ok(())
8369 })
8370 .detach_and_log_err(cx);
8371 }
8372
8373 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8374 let snapshot = self.buffer.read(cx).read(cx);
8375 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8376 Some(
8377 ranges
8378 .iter()
8379 .map(move |range| {
8380 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8381 })
8382 .collect(),
8383 )
8384 }
8385
8386 fn selection_replacement_ranges(
8387 &self,
8388 range: Range<OffsetUtf16>,
8389 cx: &AppContext,
8390 ) -> Vec<Range<OffsetUtf16>> {
8391 let selections = self.selections.all::<OffsetUtf16>(cx);
8392 let newest_selection = selections
8393 .iter()
8394 .max_by_key(|selection| selection.id)
8395 .unwrap();
8396 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8397 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8398 let snapshot = self.buffer.read(cx).read(cx);
8399 selections
8400 .into_iter()
8401 .map(|mut selection| {
8402 selection.start.0 =
8403 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8404 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8405 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8406 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8407 })
8408 .collect()
8409 }
8410
8411 fn report_copilot_event(
8412 &self,
8413 suggestion_id: Option<String>,
8414 suggestion_accepted: bool,
8415 cx: &AppContext,
8416 ) {
8417 let Some(project) = &self.project else { return };
8418
8419 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8420 let file_extension = self
8421 .buffer
8422 .read(cx)
8423 .as_singleton()
8424 .and_then(|b| b.read(cx).file())
8425 .and_then(|file| Path::new(file.file_name(cx)).extension())
8426 .and_then(|e| e.to_str())
8427 .map(|a| a.to_string());
8428
8429 let telemetry = project.read(cx).client().telemetry().clone();
8430 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8431
8432 let event = ClickhouseEvent::Copilot {
8433 suggestion_id,
8434 suggestion_accepted,
8435 file_extension,
8436 };
8437 telemetry.report_clickhouse_event(event, telemetry_settings);
8438 }
8439
8440 fn report_editor_event(
8441 &self,
8442 operation: &'static str,
8443 file_extension: Option<String>,
8444 cx: &AppContext,
8445 ) {
8446 let Some(project) = &self.project else { return };
8447
8448 // If None, we are in a file without an extension
8449 let file = self
8450 .buffer
8451 .read(cx)
8452 .as_singleton()
8453 .and_then(|b| b.read(cx).file());
8454 let file_extension = file_extension.or(file
8455 .as_ref()
8456 .and_then(|file| Path::new(file.file_name(cx)).extension())
8457 .and_then(|e| e.to_str())
8458 .map(|a| a.to_string()));
8459
8460 let vim_mode = cx
8461 .global::<SettingsStore>()
8462 .raw_user_settings()
8463 .get("vim_mode")
8464 == Some(&serde_json::Value::Bool(true));
8465 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8466 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8467 let copilot_enabled_for_language = self
8468 .buffer
8469 .read(cx)
8470 .settings_at(0, cx)
8471 .show_copilot_suggestions;
8472
8473 let telemetry = project.read(cx).client().telemetry().clone();
8474 let event = ClickhouseEvent::Editor {
8475 file_extension,
8476 vim_mode,
8477 operation,
8478 copilot_enabled,
8479 copilot_enabled_for_language,
8480 };
8481 telemetry.report_clickhouse_event(event, telemetry_settings)
8482 }
8483
8484 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8485 /// with each line being an array of {text, highlight} objects.
8486 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8487 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8488 return;
8489 };
8490
8491 #[derive(Serialize)]
8492 struct Chunk<'a> {
8493 text: String,
8494 highlight: Option<&'a str>,
8495 }
8496
8497 let snapshot = buffer.read(cx).snapshot();
8498 let range = self
8499 .selected_text_range(cx)
8500 .and_then(|selected_range| {
8501 if selected_range.is_empty() {
8502 None
8503 } else {
8504 Some(selected_range)
8505 }
8506 })
8507 .unwrap_or_else(|| 0..snapshot.len());
8508
8509 let chunks = snapshot.chunks(range, true);
8510 let mut lines = Vec::new();
8511 let mut line: VecDeque<Chunk> = VecDeque::new();
8512
8513 let theme = &theme::current(cx).editor.syntax;
8514
8515 for chunk in chunks {
8516 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8517 let mut chunk_lines = chunk.text.split("\n").peekable();
8518 while let Some(text) = chunk_lines.next() {
8519 let mut merged_with_last_token = false;
8520 if let Some(last_token) = line.back_mut() {
8521 if last_token.highlight == highlight {
8522 last_token.text.push_str(text);
8523 merged_with_last_token = true;
8524 }
8525 }
8526
8527 if !merged_with_last_token {
8528 line.push_back(Chunk {
8529 text: text.into(),
8530 highlight,
8531 });
8532 }
8533
8534 if chunk_lines.peek().is_some() {
8535 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8536 line.pop_front();
8537 }
8538 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8539 line.pop_back();
8540 }
8541
8542 lines.push(mem::take(&mut line));
8543 }
8544 }
8545 }
8546
8547 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8548 return;
8549 };
8550 cx.write_to_clipboard(ClipboardItem::new(lines));
8551 }
8552
8553 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8554 &self.inlay_hint_cache
8555 }
8556
8557 pub fn replay_insert_event(
8558 &mut self,
8559 text: &str,
8560 relative_utf16_range: Option<Range<isize>>,
8561 cx: &mut ViewContext<Self>,
8562 ) {
8563 if !self.input_enabled {
8564 cx.emit(Event::InputIgnored { text: text.into() });
8565 return;
8566 }
8567 if let Some(relative_utf16_range) = relative_utf16_range {
8568 let selections = self.selections.all::<OffsetUtf16>(cx);
8569 self.change_selections(None, cx, |s| {
8570 let new_ranges = selections.into_iter().map(|range| {
8571 let start = OffsetUtf16(
8572 range
8573 .head()
8574 .0
8575 .saturating_add_signed(relative_utf16_range.start),
8576 );
8577 let end = OffsetUtf16(
8578 range
8579 .head()
8580 .0
8581 .saturating_add_signed(relative_utf16_range.end),
8582 );
8583 start..end
8584 });
8585 s.select_ranges(new_ranges);
8586 });
8587 }
8588
8589 self.handle_input(text, cx);
8590 }
8591
8592 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
8593 let Some(project) = self.project.as_ref() else {
8594 return false;
8595 };
8596 let project = project.read(cx);
8597
8598 let mut supports = false;
8599 self.buffer().read(cx).for_each_buffer(|buffer| {
8600 if !supports {
8601 supports = project
8602 .language_servers_for_buffer(buffer.read(cx), cx)
8603 .any(
8604 |(_, server)| match server.capabilities().inlay_hint_provider {
8605 Some(lsp::OneOf::Left(enabled)) => enabled,
8606 Some(lsp::OneOf::Right(_)) => true,
8607 None => false,
8608 },
8609 )
8610 }
8611 });
8612 supports
8613 }
8614}
8615
8616fn inlay_hint_settings(
8617 location: Anchor,
8618 snapshot: &MultiBufferSnapshot,
8619 cx: &mut ViewContext<'_, '_, Editor>,
8620) -> InlayHintSettings {
8621 let file = snapshot.file_at(location);
8622 let language = snapshot.language_at(location);
8623 let settings = all_language_settings(file, cx);
8624 settings
8625 .language(language.map(|l| l.name()).as_deref())
8626 .inlay_hints
8627}
8628
8629fn consume_contiguous_rows(
8630 contiguous_row_selections: &mut Vec<Selection<Point>>,
8631 selection: &Selection<Point>,
8632 display_map: &DisplaySnapshot,
8633 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8634) -> (u32, u32) {
8635 contiguous_row_selections.push(selection.clone());
8636 let start_row = selection.start.row;
8637 let mut end_row = ending_row(selection, display_map);
8638
8639 while let Some(next_selection) = selections.peek() {
8640 if next_selection.start.row <= end_row {
8641 end_row = ending_row(next_selection, display_map);
8642 contiguous_row_selections.push(selections.next().unwrap().clone());
8643 } else {
8644 break;
8645 }
8646 }
8647 (start_row, end_row)
8648}
8649
8650fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8651 if next_selection.end.column > 0 || next_selection.is_empty() {
8652 display_map.next_line_boundary(next_selection.end).0.row + 1
8653 } else {
8654 next_selection.end.row
8655 }
8656}
8657
8658impl EditorSnapshot {
8659 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8660 self.display_snapshot.buffer_snapshot.language_at(position)
8661 }
8662
8663 pub fn is_focused(&self) -> bool {
8664 self.is_focused
8665 }
8666
8667 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8668 self.placeholder_text.as_ref()
8669 }
8670
8671 pub fn scroll_position(&self) -> Vector2F {
8672 self.scroll_anchor.scroll_position(&self.display_snapshot)
8673 }
8674}
8675
8676impl Deref for EditorSnapshot {
8677 type Target = DisplaySnapshot;
8678
8679 fn deref(&self) -> &Self::Target {
8680 &self.display_snapshot
8681 }
8682}
8683
8684#[derive(Clone, Debug, PartialEq, Eq)]
8685pub enum Event {
8686 InputIgnored {
8687 text: Arc<str>,
8688 },
8689 InputHandled {
8690 utf16_range_to_replace: Option<Range<isize>>,
8691 text: Arc<str>,
8692 },
8693 ExcerptsAdded {
8694 buffer: ModelHandle<Buffer>,
8695 predecessor: ExcerptId,
8696 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8697 },
8698 ExcerptsRemoved {
8699 ids: Vec<ExcerptId>,
8700 },
8701 BufferEdited,
8702 Edited,
8703 Reparsed,
8704 Focused,
8705 Blurred,
8706 DirtyChanged,
8707 Saved,
8708 TitleChanged,
8709 DiffBaseChanged,
8710 SelectionsChanged {
8711 local: bool,
8712 },
8713 ScrollPositionChanged {
8714 local: bool,
8715 autoscroll: bool,
8716 },
8717 Closed,
8718}
8719
8720pub struct EditorFocused(pub ViewHandle<Editor>);
8721pub struct EditorBlurred(pub ViewHandle<Editor>);
8722pub struct EditorReleased(pub WeakViewHandle<Editor>);
8723
8724impl Entity for Editor {
8725 type Event = Event;
8726
8727 fn release(&mut self, cx: &mut AppContext) {
8728 cx.emit_global(EditorReleased(self.handle.clone()));
8729 }
8730}
8731
8732impl View for Editor {
8733 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8734 let style = self.style(cx);
8735 let font_changed = self.display_map.update(cx, |map, cx| {
8736 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8737 map.set_font(style.text.font_id, style.text.font_size, cx)
8738 });
8739
8740 if font_changed {
8741 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8742 hide_hover(editor, cx);
8743 hide_link_definition(editor, cx);
8744 });
8745 }
8746
8747 Stack::new()
8748 .with_child(EditorElement::new(style.clone()))
8749 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8750 .into_any()
8751 }
8752
8753 fn ui_name() -> &'static str {
8754 "Editor"
8755 }
8756
8757 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
8758 if cx.is_self_focused() {
8759 let focused_event = EditorFocused(cx.handle());
8760 cx.emit(Event::Focused);
8761 cx.emit_global(focused_event);
8762 }
8763 if let Some(rename) = self.pending_rename.as_ref() {
8764 cx.focus(&rename.editor);
8765 } else if cx.is_self_focused() || !focused.is::<Editor>() {
8766 if !self.focused {
8767 self.blink_manager.update(cx, BlinkManager::enable);
8768 }
8769 self.focused = true;
8770 self.buffer.update(cx, |buffer, cx| {
8771 buffer.finalize_last_transaction(cx);
8772 if self.leader_replica_id.is_none() {
8773 buffer.set_active_selections(
8774 &self.selections.disjoint_anchors(),
8775 self.selections.line_mode,
8776 self.cursor_shape,
8777 cx,
8778 );
8779 }
8780 });
8781 }
8782 }
8783
8784 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8785 let blurred_event = EditorBlurred(cx.handle());
8786 cx.emit_global(blurred_event);
8787 self.focused = false;
8788 self.blink_manager.update(cx, BlinkManager::disable);
8789 self.buffer
8790 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8791 self.hide_context_menu(cx);
8792 hide_hover(self, cx);
8793 cx.emit(Event::Blurred);
8794 cx.notify();
8795 }
8796
8797 fn modifiers_changed(
8798 &mut self,
8799 event: &gpui::platform::ModifiersChangedEvent,
8800 cx: &mut ViewContext<Self>,
8801 ) -> bool {
8802 let pending_selection = self.has_pending_selection();
8803
8804 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
8805 if event.cmd && !pending_selection {
8806 let point = point.clone();
8807 let snapshot = self.snapshot(cx);
8808 let kind = point.definition_kind(event.shift);
8809
8810 show_link_definition(kind, self, point, snapshot, cx);
8811 return false;
8812 }
8813 }
8814
8815 {
8816 if self.link_go_to_definition_state.symbol_range.is_some()
8817 || !self.link_go_to_definition_state.definitions.is_empty()
8818 {
8819 self.link_go_to_definition_state.symbol_range.take();
8820 self.link_go_to_definition_state.definitions.clear();
8821 cx.notify();
8822 }
8823
8824 self.link_go_to_definition_state.task = None;
8825
8826 self.clear_highlights::<LinkGoToDefinitionState>(cx);
8827 }
8828
8829 false
8830 }
8831
8832 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8833 Self::reset_to_default_keymap_context(keymap);
8834 let mode = match self.mode {
8835 EditorMode::SingleLine => "single_line",
8836 EditorMode::AutoHeight { .. } => "auto_height",
8837 EditorMode::Full => "full",
8838 };
8839 keymap.add_key("mode", mode);
8840 if self.pending_rename.is_some() {
8841 keymap.add_identifier("renaming");
8842 }
8843 if self.context_menu_visible() {
8844 match self.context_menu.as_ref() {
8845 Some(ContextMenu::Completions(_)) => {
8846 keymap.add_identifier("menu");
8847 keymap.add_identifier("showing_completions")
8848 }
8849 Some(ContextMenu::CodeActions(_)) => {
8850 keymap.add_identifier("menu");
8851 keymap.add_identifier("showing_code_actions")
8852 }
8853 None => {}
8854 }
8855 }
8856
8857 for layer in self.keymap_context_layers.values() {
8858 keymap.extend(layer);
8859 }
8860
8861 if let Some(extension) = self
8862 .buffer
8863 .read(cx)
8864 .as_singleton()
8865 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8866 {
8867 keymap.add_key("extension", extension.to_string());
8868 }
8869 }
8870
8871 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8872 Some(
8873 self.buffer
8874 .read(cx)
8875 .read(cx)
8876 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8877 .collect(),
8878 )
8879 }
8880
8881 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8882 // Prevent the IME menu from appearing when holding down an alphabetic key
8883 // while input is disabled.
8884 if !self.input_enabled {
8885 return None;
8886 }
8887
8888 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8889 Some(range.start.0..range.end.0)
8890 }
8891
8892 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8893 let snapshot = self.buffer.read(cx).read(cx);
8894 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8895 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8896 }
8897
8898 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8899 self.clear_highlights::<InputComposition>(cx);
8900 self.ime_transaction.take();
8901 }
8902
8903 fn replace_text_in_range(
8904 &mut self,
8905 range_utf16: Option<Range<usize>>,
8906 text: &str,
8907 cx: &mut ViewContext<Self>,
8908 ) {
8909 if !self.input_enabled {
8910 cx.emit(Event::InputIgnored { text: text.into() });
8911 return;
8912 }
8913
8914 self.transact(cx, |this, cx| {
8915 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8916 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8917 Some(this.selection_replacement_ranges(range_utf16, cx))
8918 } else {
8919 this.marked_text_ranges(cx)
8920 };
8921
8922 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
8923 let newest_selection_id = this.selections.newest_anchor().id;
8924 this.selections
8925 .all::<OffsetUtf16>(cx)
8926 .iter()
8927 .zip(ranges_to_replace.iter())
8928 .find_map(|(selection, range)| {
8929 if selection.id == newest_selection_id {
8930 Some(
8931 (range.start.0 as isize - selection.head().0 as isize)
8932 ..(range.end.0 as isize - selection.head().0 as isize),
8933 )
8934 } else {
8935 None
8936 }
8937 })
8938 });
8939
8940 cx.emit(Event::InputHandled {
8941 utf16_range_to_replace: range_to_replace,
8942 text: text.into(),
8943 });
8944
8945 if let Some(new_selected_ranges) = new_selected_ranges {
8946 this.change_selections(None, cx, |selections| {
8947 selections.select_ranges(new_selected_ranges)
8948 });
8949 }
8950
8951 this.handle_input(text, cx);
8952 });
8953
8954 if let Some(transaction) = self.ime_transaction {
8955 self.buffer.update(cx, |buffer, cx| {
8956 buffer.group_until_transaction(transaction, cx);
8957 });
8958 }
8959
8960 self.unmark_text(cx);
8961 }
8962
8963 fn replace_and_mark_text_in_range(
8964 &mut self,
8965 range_utf16: Option<Range<usize>>,
8966 text: &str,
8967 new_selected_range_utf16: Option<Range<usize>>,
8968 cx: &mut ViewContext<Self>,
8969 ) {
8970 if !self.input_enabled {
8971 cx.emit(Event::InputIgnored { text: text.into() });
8972 return;
8973 }
8974
8975 let transaction = self.transact(cx, |this, cx| {
8976 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8977 let snapshot = this.buffer.read(cx).read(cx);
8978 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8979 for marked_range in &mut marked_ranges {
8980 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8981 marked_range.start.0 += relative_range_utf16.start;
8982 marked_range.start =
8983 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8984 marked_range.end =
8985 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8986 }
8987 }
8988 Some(marked_ranges)
8989 } else if let Some(range_utf16) = range_utf16 {
8990 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8991 Some(this.selection_replacement_ranges(range_utf16, cx))
8992 } else {
8993 None
8994 };
8995
8996 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
8997 let newest_selection_id = this.selections.newest_anchor().id;
8998 this.selections
8999 .all::<OffsetUtf16>(cx)
9000 .iter()
9001 .zip(ranges_to_replace.iter())
9002 .find_map(|(selection, range)| {
9003 if selection.id == newest_selection_id {
9004 Some(
9005 (range.start.0 as isize - selection.head().0 as isize)
9006 ..(range.end.0 as isize - selection.head().0 as isize),
9007 )
9008 } else {
9009 None
9010 }
9011 })
9012 });
9013
9014 cx.emit(Event::InputHandled {
9015 utf16_range_to_replace: range_to_replace,
9016 text: text.into(),
9017 });
9018
9019 if let Some(ranges) = ranges_to_replace {
9020 this.change_selections(None, cx, |s| s.select_ranges(ranges));
9021 }
9022
9023 let marked_ranges = {
9024 let snapshot = this.buffer.read(cx).read(cx);
9025 this.selections
9026 .disjoint_anchors()
9027 .iter()
9028 .map(|selection| {
9029 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
9030 })
9031 .collect::<Vec<_>>()
9032 };
9033
9034 if text.is_empty() {
9035 this.unmark_text(cx);
9036 } else {
9037 this.highlight_text::<InputComposition>(
9038 marked_ranges.clone(),
9039 this.style(cx).composition_mark,
9040 cx,
9041 );
9042 }
9043
9044 this.handle_input(text, cx);
9045
9046 if let Some(new_selected_range) = new_selected_range_utf16 {
9047 let snapshot = this.buffer.read(cx).read(cx);
9048 let new_selected_ranges = marked_ranges
9049 .into_iter()
9050 .map(|marked_range| {
9051 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9052 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9053 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9054 snapshot.clip_offset_utf16(new_start, Bias::Left)
9055 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9056 })
9057 .collect::<Vec<_>>();
9058
9059 drop(snapshot);
9060 this.change_selections(None, cx, |selections| {
9061 selections.select_ranges(new_selected_ranges)
9062 });
9063 }
9064 });
9065
9066 self.ime_transaction = self.ime_transaction.or(transaction);
9067 if let Some(transaction) = self.ime_transaction {
9068 self.buffer.update(cx, |buffer, cx| {
9069 buffer.group_until_transaction(transaction, cx);
9070 });
9071 }
9072
9073 if self.text_highlights::<InputComposition>(cx).is_none() {
9074 self.ime_transaction.take();
9075 }
9076 }
9077}
9078
9079fn build_style(
9080 settings: &ThemeSettings,
9081 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9082 override_text_style: Option<&OverrideTextStyle>,
9083 cx: &AppContext,
9084) -> EditorStyle {
9085 let font_cache = cx.font_cache();
9086 let line_height_scalar = settings.line_height();
9087 let theme_id = settings.theme.meta.id;
9088 let mut theme = settings.theme.editor.clone();
9089 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9090 let field_editor_theme = get_field_editor_theme(&settings.theme);
9091 theme.text_color = field_editor_theme.text.color;
9092 theme.selection = field_editor_theme.selection;
9093 theme.background = field_editor_theme
9094 .container
9095 .background_color
9096 .unwrap_or_default();
9097 EditorStyle {
9098 text: field_editor_theme.text,
9099 placeholder_text: field_editor_theme.placeholder_text,
9100 line_height_scalar,
9101 theme,
9102 theme_id,
9103 }
9104 } else {
9105 let font_family_id = settings.buffer_font_family;
9106 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9107 let font_properties = Default::default();
9108 let font_id = font_cache
9109 .select_font(font_family_id, &font_properties)
9110 .unwrap();
9111 let font_size = settings.buffer_font_size(cx);
9112 EditorStyle {
9113 text: TextStyle {
9114 color: settings.theme.editor.text_color,
9115 font_family_name,
9116 font_family_id,
9117 font_id,
9118 font_size,
9119 font_properties,
9120 underline: Default::default(),
9121 soft_wrap: false,
9122 },
9123 placeholder_text: None,
9124 line_height_scalar,
9125 theme,
9126 theme_id,
9127 }
9128 };
9129
9130 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9131 if let Some(highlighted) = style
9132 .text
9133 .clone()
9134 .highlight(highlight_style, font_cache)
9135 .log_err()
9136 {
9137 style.text = highlighted;
9138 }
9139 }
9140
9141 style
9142}
9143
9144trait SelectionExt {
9145 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9146 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9147 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9148 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9149 -> Range<u32>;
9150}
9151
9152impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9153 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9154 let start = self.start.to_point(buffer);
9155 let end = self.end.to_point(buffer);
9156 if self.reversed {
9157 end..start
9158 } else {
9159 start..end
9160 }
9161 }
9162
9163 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9164 let start = self.start.to_offset(buffer);
9165 let end = self.end.to_offset(buffer);
9166 if self.reversed {
9167 end..start
9168 } else {
9169 start..end
9170 }
9171 }
9172
9173 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9174 let start = self
9175 .start
9176 .to_point(&map.buffer_snapshot)
9177 .to_display_point(map);
9178 let end = self
9179 .end
9180 .to_point(&map.buffer_snapshot)
9181 .to_display_point(map);
9182 if self.reversed {
9183 end..start
9184 } else {
9185 start..end
9186 }
9187 }
9188
9189 fn spanned_rows(
9190 &self,
9191 include_end_if_at_line_start: bool,
9192 map: &DisplaySnapshot,
9193 ) -> Range<u32> {
9194 let start = self.start.to_point(&map.buffer_snapshot);
9195 let mut end = self.end.to_point(&map.buffer_snapshot);
9196 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9197 end.row -= 1;
9198 }
9199
9200 let buffer_start = map.prev_line_boundary(start).0;
9201 let buffer_end = map.next_line_boundary(end).0;
9202 buffer_start.row..buffer_end.row + 1
9203 }
9204}
9205
9206impl<T: InvalidationRegion> InvalidationStack<T> {
9207 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9208 where
9209 S: Clone + ToOffset,
9210 {
9211 while let Some(region) = self.last() {
9212 let all_selections_inside_invalidation_ranges =
9213 if selections.len() == region.ranges().len() {
9214 selections
9215 .iter()
9216 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9217 .all(|(selection, invalidation_range)| {
9218 let head = selection.head().to_offset(buffer);
9219 invalidation_range.start <= head && invalidation_range.end >= head
9220 })
9221 } else {
9222 false
9223 };
9224
9225 if all_selections_inside_invalidation_ranges {
9226 break;
9227 } else {
9228 self.pop();
9229 }
9230 }
9231 }
9232}
9233
9234impl<T> Default for InvalidationStack<T> {
9235 fn default() -> Self {
9236 Self(Default::default())
9237 }
9238}
9239
9240impl<T> Deref for InvalidationStack<T> {
9241 type Target = Vec<T>;
9242
9243 fn deref(&self) -> &Self::Target {
9244 &self.0
9245 }
9246}
9247
9248impl<T> DerefMut for InvalidationStack<T> {
9249 fn deref_mut(&mut self) -> &mut Self::Target {
9250 &mut self.0
9251 }
9252}
9253
9254impl InvalidationRegion for SnippetState {
9255 fn ranges(&self) -> &[Range<Anchor>] {
9256 &self.ranges[self.active_index]
9257 }
9258}
9259
9260impl Deref for EditorStyle {
9261 type Target = theme::Editor;
9262
9263 fn deref(&self) -> &Self::Target {
9264 &self.theme
9265 }
9266}
9267
9268pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9269 let mut highlighted_lines = Vec::new();
9270
9271 for (index, line) in diagnostic.message.lines().enumerate() {
9272 let line = match &diagnostic.source {
9273 Some(source) if index == 0 => {
9274 let source_highlight = Vec::from_iter(0..source.len());
9275 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9276 }
9277
9278 _ => highlight_diagnostic_message(Vec::new(), line),
9279 };
9280 highlighted_lines.push(line);
9281 }
9282 let message = diagnostic.message;
9283 Arc::new(move |cx: &mut BlockContext| {
9284 let message = message.clone();
9285 let settings = settings::get::<ThemeSettings>(cx);
9286 let tooltip_style = settings.theme.tooltip.clone();
9287 let theme = &settings.theme.editor;
9288 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9289 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9290 let anchor_x = cx.anchor_x;
9291 enum BlockContextToolip {}
9292 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9293 Flex::column()
9294 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9295 Label::new(
9296 line.clone(),
9297 style.message.clone().with_font_size(font_size),
9298 )
9299 .with_highlights(highlights.clone())
9300 .contained()
9301 .with_margin_left(anchor_x)
9302 }))
9303 .aligned()
9304 .left()
9305 .into_any()
9306 })
9307 .with_cursor_style(CursorStyle::PointingHand)
9308 .on_click(MouseButton::Left, move |_, _, cx| {
9309 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9310 })
9311 // We really need to rethink this ID system...
9312 .with_tooltip::<BlockContextToolip>(
9313 cx.block_id,
9314 "Copy diagnostic message",
9315 None,
9316 tooltip_style,
9317 cx,
9318 )
9319 .into_any()
9320 })
9321}
9322
9323pub fn highlight_diagnostic_message(
9324 initial_highlights: Vec<usize>,
9325 message: &str,
9326) -> (String, Vec<usize>) {
9327 let mut message_without_backticks = String::new();
9328 let mut prev_offset = 0;
9329 let mut inside_block = false;
9330 let mut highlights = initial_highlights;
9331 for (match_ix, (offset, _)) in message
9332 .match_indices('`')
9333 .chain([(message.len(), "")])
9334 .enumerate()
9335 {
9336 message_without_backticks.push_str(&message[prev_offset..offset]);
9337 if inside_block {
9338 highlights.extend(prev_offset - match_ix..offset - match_ix);
9339 }
9340
9341 inside_block = !inside_block;
9342 prev_offset = offset + 1;
9343 }
9344
9345 (message_without_backticks, highlights)
9346}
9347
9348pub fn diagnostic_style(
9349 severity: DiagnosticSeverity,
9350 valid: bool,
9351 theme: &theme::Editor,
9352) -> DiagnosticStyle {
9353 match (severity, valid) {
9354 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9355 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9356 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9357 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9358 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9359 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9360 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9361 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9362 _ => theme.invalid_hint_diagnostic.clone(),
9363 }
9364}
9365
9366pub fn combine_syntax_and_fuzzy_match_highlights(
9367 text: &str,
9368 default_style: HighlightStyle,
9369 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9370 match_indices: &[usize],
9371) -> Vec<(Range<usize>, HighlightStyle)> {
9372 let mut result = Vec::new();
9373 let mut match_indices = match_indices.iter().copied().peekable();
9374
9375 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9376 {
9377 syntax_highlight.weight = None;
9378
9379 // Add highlights for any fuzzy match characters before the next
9380 // syntax highlight range.
9381 while let Some(&match_index) = match_indices.peek() {
9382 if match_index >= range.start {
9383 break;
9384 }
9385 match_indices.next();
9386 let end_index = char_ix_after(match_index, text);
9387 let mut match_style = default_style;
9388 match_style.weight = Some(fonts::Weight::BOLD);
9389 result.push((match_index..end_index, match_style));
9390 }
9391
9392 if range.start == usize::MAX {
9393 break;
9394 }
9395
9396 // Add highlights for any fuzzy match characters within the
9397 // syntax highlight range.
9398 let mut offset = range.start;
9399 while let Some(&match_index) = match_indices.peek() {
9400 if match_index >= range.end {
9401 break;
9402 }
9403
9404 match_indices.next();
9405 if match_index > offset {
9406 result.push((offset..match_index, syntax_highlight));
9407 }
9408
9409 let mut end_index = char_ix_after(match_index, text);
9410 while let Some(&next_match_index) = match_indices.peek() {
9411 if next_match_index == end_index && next_match_index < range.end {
9412 end_index = char_ix_after(next_match_index, text);
9413 match_indices.next();
9414 } else {
9415 break;
9416 }
9417 }
9418
9419 let mut match_style = syntax_highlight;
9420 match_style.weight = Some(fonts::Weight::BOLD);
9421 result.push((match_index..end_index, match_style));
9422 offset = end_index;
9423 }
9424
9425 if offset < range.end {
9426 result.push((offset..range.end, syntax_highlight));
9427 }
9428 }
9429
9430 fn char_ix_after(ix: usize, text: &str) -> usize {
9431 ix + text[ix..].chars().next().unwrap().len_utf8()
9432 }
9433
9434 result
9435}
9436
9437pub fn styled_runs_for_code_label<'a>(
9438 label: &'a CodeLabel,
9439 syntax_theme: &'a theme::SyntaxTheme,
9440) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9441 let fade_out = HighlightStyle {
9442 fade_out: Some(0.35),
9443 ..Default::default()
9444 };
9445
9446 let mut prev_end = label.filter_range.end;
9447 label
9448 .runs
9449 .iter()
9450 .enumerate()
9451 .flat_map(move |(ix, (range, highlight_id))| {
9452 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9453 style
9454 } else {
9455 return Default::default();
9456 };
9457 let mut muted_style = style;
9458 muted_style.highlight(fade_out);
9459
9460 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9461 if range.start >= label.filter_range.end {
9462 if range.start > prev_end {
9463 runs.push((prev_end..range.start, fade_out));
9464 }
9465 runs.push((range.clone(), muted_style));
9466 } else if range.end <= label.filter_range.end {
9467 runs.push((range.clone(), style));
9468 } else {
9469 runs.push((range.start..label.filter_range.end, style));
9470 runs.push((label.filter_range.end..range.end, muted_style));
9471 }
9472 prev_end = cmp::max(prev_end, range.end);
9473
9474 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9475 runs.push((prev_end..label.text.len(), fade_out));
9476 }
9477
9478 runs
9479 })
9480}
9481
9482pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9483 let mut index = 0;
9484 let mut codepoints = text.char_indices().peekable();
9485
9486 std::iter::from_fn(move || {
9487 let start_index = index;
9488 while let Some((new_index, codepoint)) = codepoints.next() {
9489 index = new_index + codepoint.len_utf8();
9490 let current_upper = codepoint.is_uppercase();
9491 let next_upper = codepoints
9492 .peek()
9493 .map(|(_, c)| c.is_uppercase())
9494 .unwrap_or(false);
9495
9496 if !current_upper && next_upper {
9497 return Some(&text[start_index..index]);
9498 }
9499 }
9500
9501 index = text.len();
9502 if start_index < text.len() {
9503 return Some(&text[start_index..]);
9504 }
9505 None
9506 })
9507 .flat_map(|word| word.split_inclusive('_'))
9508 .flat_map(|word| word.split_inclusive('-'))
9509}
9510
9511trait RangeToAnchorExt {
9512 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9513}
9514
9515impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9516 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9517 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9518 }
9519}