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