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