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 self.insert_with_autoindent_mode(
2518 text,
2519 Some(AutoindentMode::Block {
2520 original_indent_columns: Vec::new(),
2521 }),
2522 cx,
2523 );
2524 }
2525
2526 fn insert_with_autoindent_mode(
2527 &mut self,
2528 text: &str,
2529 autoindent_mode: Option<AutoindentMode>,
2530 cx: &mut ViewContext<Self>,
2531 ) {
2532 if self.read_only {
2533 return;
2534 }
2535
2536 let text: Arc<str> = text.into();
2537 self.transact(cx, |this, cx| {
2538 let old_selections = this.selections.all_adjusted(cx);
2539 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2540 let anchors = {
2541 let snapshot = buffer.read(cx);
2542 old_selections
2543 .iter()
2544 .map(|s| {
2545 let anchor = snapshot.anchor_after(s.head());
2546 s.map(|_| anchor)
2547 })
2548 .collect::<Vec<_>>()
2549 };
2550 buffer.edit(
2551 old_selections
2552 .iter()
2553 .map(|s| (s.start..s.end, text.clone())),
2554 autoindent_mode,
2555 cx,
2556 );
2557 anchors
2558 });
2559
2560 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2561 s.select_anchors(selection_anchors);
2562 })
2563 });
2564 }
2565
2566 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2567 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2568 return;
2569 }
2570
2571 let selection = self.selections.newest_anchor();
2572 if self
2573 .buffer
2574 .read(cx)
2575 .is_completion_trigger(selection.head(), text, cx)
2576 {
2577 self.show_completions(&ShowCompletions, cx);
2578 } else {
2579 self.hide_context_menu(cx);
2580 }
2581 }
2582
2583 /// If any empty selections is touching the start of its innermost containing autoclose
2584 /// region, expand it to select the brackets.
2585 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2586 let selections = self.selections.all::<usize>(cx);
2587 let buffer = self.buffer.read(cx).read(cx);
2588 let mut new_selections = Vec::new();
2589 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2590 if let (Some(region), true) = (region, selection.is_empty()) {
2591 let mut range = region.range.to_offset(&buffer);
2592 if selection.start == range.start {
2593 if range.start >= region.pair.start.len() {
2594 range.start -= region.pair.start.len();
2595 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2596 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2597 range.end += region.pair.end.len();
2598 selection.start = range.start;
2599 selection.end = range.end;
2600 }
2601 }
2602 }
2603 }
2604 }
2605 new_selections.push(selection);
2606 }
2607
2608 drop(buffer);
2609 self.change_selections(None, cx, |selections| selections.select(new_selections));
2610 }
2611
2612 /// Iterate the given selections, and for each one, find the smallest surrounding
2613 /// autoclose region. This uses the ordering of the selections and the autoclose
2614 /// regions to avoid repeated comparisons.
2615 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2616 &'a self,
2617 selections: impl IntoIterator<Item = Selection<D>>,
2618 buffer: &'a MultiBufferSnapshot,
2619 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2620 let mut i = 0;
2621 let mut regions = self.autoclose_regions.as_slice();
2622 selections.into_iter().map(move |selection| {
2623 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2624
2625 let mut enclosing = None;
2626 while let Some(pair_state) = regions.get(i) {
2627 if pair_state.range.end.to_offset(buffer) < range.start {
2628 regions = ®ions[i + 1..];
2629 i = 0;
2630 } else if pair_state.range.start.to_offset(buffer) > range.end {
2631 break;
2632 } else if pair_state.selection_id == selection.id {
2633 enclosing = Some(pair_state);
2634 i += 1;
2635 }
2636 }
2637
2638 (selection.clone(), enclosing)
2639 })
2640 }
2641
2642 /// Remove any autoclose regions that no longer contain their selection.
2643 fn invalidate_autoclose_regions(
2644 &mut self,
2645 mut selections: &[Selection<Anchor>],
2646 buffer: &MultiBufferSnapshot,
2647 ) {
2648 self.autoclose_regions.retain(|state| {
2649 let mut i = 0;
2650 while let Some(selection) = selections.get(i) {
2651 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2652 selections = &selections[1..];
2653 continue;
2654 }
2655 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2656 break;
2657 }
2658 if selection.id == state.selection_id {
2659 return true;
2660 } else {
2661 i += 1;
2662 }
2663 }
2664 false
2665 });
2666 }
2667
2668 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2669 let offset = position.to_offset(buffer);
2670 let (word_range, kind) = buffer.surrounding_word(offset);
2671 if offset > word_range.start && kind == Some(CharKind::Word) {
2672 Some(
2673 buffer
2674 .text_for_range(word_range.start..offset)
2675 .collect::<String>(),
2676 )
2677 } else {
2678 None
2679 }
2680 }
2681
2682 fn refresh_inlays(&mut self, reason: InlayRefreshReason, cx: &mut ViewContext<Self>) {
2683 if self.project.is_none() || self.mode != EditorMode::Full {
2684 return;
2685 }
2686
2687 let (invalidate_cache, required_languages) = match reason {
2688 InlayRefreshReason::SettingsChange(new_settings) => {
2689 match self.inlay_hint_cache.update_settings(
2690 &self.buffer,
2691 new_settings,
2692 self.visible_inlay_hints(cx),
2693 cx,
2694 ) {
2695 ControlFlow::Break(Some(InlaySplice {
2696 to_remove,
2697 to_insert,
2698 })) => {
2699 self.splice_inlay_hints(to_remove, to_insert, cx);
2700 return;
2701 }
2702 ControlFlow::Break(None) => return,
2703 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2704 }
2705 }
2706 InlayRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2707 InlayRefreshReason::BufferEdited(buffer_languages) => {
2708 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2709 }
2710 InlayRefreshReason::RefreshRequested => (InvalidationStrategy::RefreshRequested, None),
2711 };
2712
2713 if let Some(InlaySplice {
2714 to_remove,
2715 to_insert,
2716 }) = self.inlay_hint_cache.spawn_hint_refresh(
2717 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
2718 invalidate_cache,
2719 cx,
2720 ) {
2721 self.splice_inlay_hints(to_remove, to_insert, cx);
2722 }
2723 }
2724
2725 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
2726 self.display_map
2727 .read(cx)
2728 .current_inlays()
2729 .filter(move |inlay| {
2730 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
2731 })
2732 .cloned()
2733 .collect()
2734 }
2735
2736 pub fn excerpt_visible_offsets(
2737 &self,
2738 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
2739 cx: &mut ViewContext<'_, '_, Editor>,
2740 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
2741 let multi_buffer = self.buffer().read(cx);
2742 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2743 let multi_buffer_visible_start = self
2744 .scroll_manager
2745 .anchor()
2746 .anchor
2747 .to_point(&multi_buffer_snapshot);
2748 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
2749 multi_buffer_visible_start
2750 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
2751 Bias::Left,
2752 );
2753 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
2754 multi_buffer
2755 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
2756 .into_iter()
2757 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
2758 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
2759 let buffer = buffer_handle.read(cx);
2760 let language = buffer.language()?;
2761 if let Some(restrict_to_languages) = restrict_to_languages {
2762 if !restrict_to_languages.contains(language) {
2763 return None;
2764 }
2765 }
2766 Some((
2767 excerpt_id,
2768 (
2769 buffer_handle,
2770 buffer.version().clone(),
2771 excerpt_visible_range,
2772 ),
2773 ))
2774 })
2775 .collect()
2776 }
2777
2778 fn splice_inlay_hints(
2779 &self,
2780 to_remove: Vec<InlayId>,
2781 to_insert: Vec<Inlay>,
2782 cx: &mut ViewContext<Self>,
2783 ) {
2784 self.display_map.update(cx, |display_map, cx| {
2785 display_map.splice_inlays(to_remove, to_insert, cx);
2786 });
2787 }
2788
2789 fn trigger_on_type_formatting(
2790 &self,
2791 input: String,
2792 cx: &mut ViewContext<Self>,
2793 ) -> Option<Task<Result<()>>> {
2794 if input.len() != 1 {
2795 return None;
2796 }
2797
2798 let project = self.project.as_ref()?;
2799 let position = self.selections.newest_anchor().head();
2800 let (buffer, buffer_position) = self
2801 .buffer
2802 .read(cx)
2803 .text_anchor_for_position(position.clone(), cx)?;
2804
2805 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2806 // hence we do LSP request & edit on host side only — add formats to host's history.
2807 let push_to_lsp_host_history = true;
2808 // If this is not the host, append its history with new edits.
2809 let push_to_client_history = project.read(cx).is_remote();
2810
2811 let on_type_formatting = project.update(cx, |project, cx| {
2812 project.on_type_format(
2813 buffer.clone(),
2814 buffer_position,
2815 input,
2816 push_to_lsp_host_history,
2817 cx,
2818 )
2819 });
2820 Some(cx.spawn(|editor, mut cx| async move {
2821 if let Some(transaction) = on_type_formatting.await? {
2822 if push_to_client_history {
2823 buffer.update(&mut cx, |buffer, _| {
2824 buffer.push_transaction(transaction, Instant::now());
2825 });
2826 }
2827 editor.update(&mut cx, |editor, cx| {
2828 editor.refresh_document_highlights(cx);
2829 })?;
2830 }
2831 Ok(())
2832 }))
2833 }
2834
2835 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2836 if self.pending_rename.is_some() {
2837 return;
2838 }
2839
2840 let project = if let Some(project) = self.project.clone() {
2841 project
2842 } else {
2843 return;
2844 };
2845
2846 let position = self.selections.newest_anchor().head();
2847 let (buffer, buffer_position) = if let Some(output) = self
2848 .buffer
2849 .read(cx)
2850 .text_anchor_for_position(position.clone(), cx)
2851 {
2852 output
2853 } else {
2854 return;
2855 };
2856
2857 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2858 let completions = project.update(cx, |project, cx| {
2859 project.completions(&buffer, buffer_position, cx)
2860 });
2861
2862 let id = post_inc(&mut self.next_completion_id);
2863 let task = cx.spawn(|this, mut cx| {
2864 async move {
2865 let menu = if let Some(completions) = completions.await.log_err() {
2866 let mut menu = CompletionsMenu {
2867 id,
2868 initial_position: position,
2869 match_candidates: completions
2870 .iter()
2871 .enumerate()
2872 .map(|(id, completion)| {
2873 StringMatchCandidate::new(
2874 id,
2875 completion.label.text[completion.label.filter_range.clone()]
2876 .into(),
2877 )
2878 })
2879 .collect(),
2880 buffer,
2881 completions: completions.into(),
2882 matches: Vec::new().into(),
2883 selected_item: 0,
2884 list: Default::default(),
2885 };
2886 menu.filter(query.as_deref(), cx.background()).await;
2887 if menu.matches.is_empty() {
2888 None
2889 } else {
2890 Some(menu)
2891 }
2892 } else {
2893 None
2894 };
2895
2896 this.update(&mut cx, |this, cx| {
2897 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2898
2899 match this.context_menu.as_ref() {
2900 None => {}
2901 Some(ContextMenu::Completions(prev_menu)) => {
2902 if prev_menu.id > id {
2903 return;
2904 }
2905 }
2906 _ => return,
2907 }
2908
2909 if this.focused && menu.is_some() {
2910 let menu = menu.unwrap();
2911 this.show_context_menu(ContextMenu::Completions(menu), cx);
2912 } else if this.completion_tasks.is_empty() {
2913 // If there are no more completion tasks and the last menu was
2914 // empty, we should hide it. If it was already hidden, we should
2915 // also show the copilot suggestion when available.
2916 if this.hide_context_menu(cx).is_none() {
2917 this.update_visible_copilot_suggestion(cx);
2918 }
2919 }
2920 })?;
2921
2922 Ok::<_, anyhow::Error>(())
2923 }
2924 .log_err()
2925 });
2926 self.completion_tasks.push((id, task));
2927 }
2928
2929 pub fn confirm_completion(
2930 &mut self,
2931 action: &ConfirmCompletion,
2932 cx: &mut ViewContext<Self>,
2933 ) -> Option<Task<Result<()>>> {
2934 use language::ToOffset as _;
2935
2936 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2937 menu
2938 } else {
2939 return None;
2940 };
2941
2942 let mat = completions_menu
2943 .matches
2944 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2945 let buffer_handle = completions_menu.buffer;
2946 let completion = completions_menu.completions.get(mat.candidate_id)?;
2947
2948 let snippet;
2949 let text;
2950 if completion.is_snippet() {
2951 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2952 text = snippet.as_ref().unwrap().text.clone();
2953 } else {
2954 snippet = None;
2955 text = completion.new_text.clone();
2956 };
2957 let selections = self.selections.all::<usize>(cx);
2958 let buffer = buffer_handle.read(cx);
2959 let old_range = completion.old_range.to_offset(buffer);
2960 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2961
2962 let newest_selection = self.selections.newest_anchor();
2963 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2964 return None;
2965 }
2966
2967 let lookbehind = newest_selection
2968 .start
2969 .text_anchor
2970 .to_offset(buffer)
2971 .saturating_sub(old_range.start);
2972 let lookahead = old_range
2973 .end
2974 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2975 let mut common_prefix_len = old_text
2976 .bytes()
2977 .zip(text.bytes())
2978 .take_while(|(a, b)| a == b)
2979 .count();
2980
2981 let snapshot = self.buffer.read(cx).snapshot(cx);
2982 let mut ranges = Vec::new();
2983 for selection in &selections {
2984 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2985 let start = selection.start.saturating_sub(lookbehind);
2986 let end = selection.end + lookahead;
2987 ranges.push(start + common_prefix_len..end);
2988 } else {
2989 common_prefix_len = 0;
2990 ranges.clear();
2991 ranges.extend(selections.iter().map(|s| {
2992 if s.id == newest_selection.id {
2993 old_range.clone()
2994 } else {
2995 s.start..s.end
2996 }
2997 }));
2998 break;
2999 }
3000 }
3001 let text = &text[common_prefix_len..];
3002
3003 self.transact(cx, |this, cx| {
3004 if let Some(mut snippet) = snippet {
3005 snippet.text = text.to_string();
3006 for tabstop in snippet.tabstops.iter_mut().flatten() {
3007 tabstop.start -= common_prefix_len as isize;
3008 tabstop.end -= common_prefix_len as isize;
3009 }
3010
3011 this.insert_snippet(&ranges, snippet, cx).log_err();
3012 } else {
3013 this.buffer.update(cx, |buffer, cx| {
3014 buffer.edit(
3015 ranges.iter().map(|range| (range.clone(), text)),
3016 this.autoindent_mode.clone(),
3017 cx,
3018 );
3019 });
3020 }
3021
3022 this.refresh_copilot_suggestions(true, cx);
3023 });
3024
3025 let project = self.project.clone()?;
3026 let apply_edits = project.update(cx, |project, cx| {
3027 project.apply_additional_edits_for_completion(
3028 buffer_handle,
3029 completion.clone(),
3030 true,
3031 cx,
3032 )
3033 });
3034 Some(cx.foreground().spawn(async move {
3035 apply_edits.await?;
3036 Ok(())
3037 }))
3038 }
3039
3040 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3041 if matches!(
3042 self.context_menu.as_ref(),
3043 Some(ContextMenu::CodeActions(_))
3044 ) {
3045 self.context_menu.take();
3046 cx.notify();
3047 return;
3048 }
3049
3050 let deployed_from_indicator = action.deployed_from_indicator;
3051 let mut task = self.code_actions_task.take();
3052 cx.spawn(|this, mut cx| async move {
3053 while let Some(prev_task) = task {
3054 prev_task.await;
3055 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3056 }
3057
3058 this.update(&mut cx, |this, cx| {
3059 if this.focused {
3060 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3061 this.show_context_menu(
3062 ContextMenu::CodeActions(CodeActionsMenu {
3063 buffer,
3064 actions,
3065 selected_item: Default::default(),
3066 list: Default::default(),
3067 deployed_from_indicator,
3068 }),
3069 cx,
3070 );
3071 }
3072 }
3073 })?;
3074
3075 Ok::<_, anyhow::Error>(())
3076 })
3077 .detach_and_log_err(cx);
3078 }
3079
3080 pub fn confirm_code_action(
3081 workspace: &mut Workspace,
3082 action: &ConfirmCodeAction,
3083 cx: &mut ViewContext<Workspace>,
3084 ) -> Option<Task<Result<()>>> {
3085 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3086 let actions_menu = if let ContextMenu::CodeActions(menu) =
3087 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3088 {
3089 menu
3090 } else {
3091 return None;
3092 };
3093 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3094 let action = actions_menu.actions.get(action_ix)?.clone();
3095 let title = action.lsp_action.title.clone();
3096 let buffer = actions_menu.buffer;
3097
3098 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3099 project.apply_code_action(buffer, action, true, cx)
3100 });
3101 let editor = editor.downgrade();
3102 Some(cx.spawn(|workspace, cx| async move {
3103 let project_transaction = apply_code_actions.await?;
3104 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3105 }))
3106 }
3107
3108 async fn open_project_transaction(
3109 this: &WeakViewHandle<Editor>,
3110 workspace: WeakViewHandle<Workspace>,
3111 transaction: ProjectTransaction,
3112 title: String,
3113 mut cx: AsyncAppContext,
3114 ) -> Result<()> {
3115 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3116
3117 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3118 entries.sort_unstable_by_key(|(buffer, _)| {
3119 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3120 });
3121
3122 // If the project transaction's edits are all contained within this editor, then
3123 // avoid opening a new editor to display them.
3124
3125 if let Some((buffer, transaction)) = entries.first() {
3126 if entries.len() == 1 {
3127 let excerpt = this.read_with(&cx, |editor, cx| {
3128 editor
3129 .buffer()
3130 .read(cx)
3131 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3132 })?;
3133 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3134 if excerpted_buffer == *buffer {
3135 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3136 let excerpt_range = excerpt_range.to_offset(buffer);
3137 buffer
3138 .edited_ranges_for_transaction::<usize>(transaction)
3139 .all(|range| {
3140 excerpt_range.start <= range.start
3141 && excerpt_range.end >= range.end
3142 })
3143 });
3144
3145 if all_edits_within_excerpt {
3146 return Ok(());
3147 }
3148 }
3149 }
3150 }
3151 } else {
3152 return Ok(());
3153 }
3154
3155 let mut ranges_to_highlight = Vec::new();
3156 let excerpt_buffer = cx.add_model(|cx| {
3157 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3158 for (buffer_handle, transaction) in &entries {
3159 let buffer = buffer_handle.read(cx);
3160 ranges_to_highlight.extend(
3161 multibuffer.push_excerpts_with_context_lines(
3162 buffer_handle.clone(),
3163 buffer
3164 .edited_ranges_for_transaction::<usize>(transaction)
3165 .collect(),
3166 1,
3167 cx,
3168 ),
3169 );
3170 }
3171 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3172 multibuffer
3173 });
3174
3175 workspace.update(&mut cx, |workspace, cx| {
3176 let project = workspace.project().clone();
3177 let editor =
3178 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3179 workspace.add_item(Box::new(editor.clone()), cx);
3180 editor.update(cx, |editor, cx| {
3181 editor.highlight_background::<Self>(
3182 ranges_to_highlight,
3183 |theme| theme.editor.highlighted_line_background,
3184 cx,
3185 );
3186 });
3187 })?;
3188
3189 Ok(())
3190 }
3191
3192 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3193 let project = self.project.as_ref()?;
3194 let buffer = self.buffer.read(cx);
3195 let newest_selection = self.selections.newest_anchor().clone();
3196 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3197 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3198 if start_buffer != end_buffer {
3199 return None;
3200 }
3201
3202 let actions = project.update(cx, |project, cx| {
3203 project.code_actions(&start_buffer, start..end, cx)
3204 });
3205 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3206 let actions = actions.await;
3207 this.update(&mut cx, |this, cx| {
3208 this.available_code_actions = actions.log_err().and_then(|actions| {
3209 if actions.is_empty() {
3210 None
3211 } else {
3212 Some((start_buffer, actions.into()))
3213 }
3214 });
3215 cx.notify();
3216 })
3217 .log_err();
3218 }));
3219 None
3220 }
3221
3222 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3223 if self.pending_rename.is_some() {
3224 return None;
3225 }
3226
3227 let project = self.project.as_ref()?;
3228 let buffer = self.buffer.read(cx);
3229 let newest_selection = self.selections.newest_anchor().clone();
3230 let cursor_position = newest_selection.head();
3231 let (cursor_buffer, cursor_buffer_position) =
3232 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3233 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3234 if cursor_buffer != tail_buffer {
3235 return None;
3236 }
3237
3238 let highlights = project.update(cx, |project, cx| {
3239 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3240 });
3241
3242 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3243 if let Some(highlights) = highlights.await.log_err() {
3244 this.update(&mut cx, |this, cx| {
3245 if this.pending_rename.is_some() {
3246 return;
3247 }
3248
3249 let buffer_id = cursor_position.buffer_id;
3250 let buffer = this.buffer.read(cx);
3251 if !buffer
3252 .text_anchor_for_position(cursor_position, cx)
3253 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3254 {
3255 return;
3256 }
3257
3258 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3259 let mut write_ranges = Vec::new();
3260 let mut read_ranges = Vec::new();
3261 for highlight in highlights {
3262 for (excerpt_id, excerpt_range) in
3263 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3264 {
3265 let start = highlight
3266 .range
3267 .start
3268 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3269 let end = highlight
3270 .range
3271 .end
3272 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3273 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3274 continue;
3275 }
3276
3277 let range = Anchor {
3278 buffer_id,
3279 excerpt_id: excerpt_id.clone(),
3280 text_anchor: start,
3281 }..Anchor {
3282 buffer_id,
3283 excerpt_id,
3284 text_anchor: end,
3285 };
3286 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3287 write_ranges.push(range);
3288 } else {
3289 read_ranges.push(range);
3290 }
3291 }
3292 }
3293
3294 this.highlight_background::<DocumentHighlightRead>(
3295 read_ranges,
3296 |theme| theme.editor.document_highlight_read_background,
3297 cx,
3298 );
3299 this.highlight_background::<DocumentHighlightWrite>(
3300 write_ranges,
3301 |theme| theme.editor.document_highlight_write_background,
3302 cx,
3303 );
3304 cx.notify();
3305 })
3306 .log_err();
3307 }
3308 }));
3309 None
3310 }
3311
3312 fn refresh_copilot_suggestions(
3313 &mut self,
3314 debounce: bool,
3315 cx: &mut ViewContext<Self>,
3316 ) -> Option<()> {
3317 let copilot = Copilot::global(cx)?;
3318 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3319 self.clear_copilot_suggestions(cx);
3320 return None;
3321 }
3322 self.update_visible_copilot_suggestion(cx);
3323
3324 let snapshot = self.buffer.read(cx).snapshot(cx);
3325 let cursor = self.selections.newest_anchor().head();
3326 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3327 self.clear_copilot_suggestions(cx);
3328 return None;
3329 }
3330
3331 let (buffer, buffer_position) =
3332 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3333 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3334 if debounce {
3335 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3336 }
3337
3338 let completions = copilot
3339 .update(&mut cx, |copilot, cx| {
3340 copilot.completions(&buffer, buffer_position, cx)
3341 })
3342 .await
3343 .log_err()
3344 .into_iter()
3345 .flatten()
3346 .collect_vec();
3347
3348 this.update(&mut cx, |this, cx| {
3349 if !completions.is_empty() {
3350 this.copilot_state.cycled = false;
3351 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3352 this.copilot_state.completions.clear();
3353 this.copilot_state.active_completion_index = 0;
3354 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3355 for completion in completions {
3356 this.copilot_state.push_completion(completion);
3357 }
3358 this.update_visible_copilot_suggestion(cx);
3359 }
3360 })
3361 .log_err()?;
3362 Some(())
3363 });
3364
3365 Some(())
3366 }
3367
3368 fn cycle_copilot_suggestions(
3369 &mut self,
3370 direction: Direction,
3371 cx: &mut ViewContext<Self>,
3372 ) -> Option<()> {
3373 let copilot = Copilot::global(cx)?;
3374 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3375 return None;
3376 }
3377
3378 if self.copilot_state.cycled {
3379 self.copilot_state.cycle_completions(direction);
3380 self.update_visible_copilot_suggestion(cx);
3381 } else {
3382 let cursor = self.selections.newest_anchor().head();
3383 let (buffer, buffer_position) =
3384 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3385 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3386 let completions = copilot
3387 .update(&mut cx, |copilot, cx| {
3388 copilot.completions_cycling(&buffer, buffer_position, cx)
3389 })
3390 .await;
3391
3392 this.update(&mut cx, |this, cx| {
3393 this.copilot_state.cycled = true;
3394 for completion in completions.log_err().into_iter().flatten() {
3395 this.copilot_state.push_completion(completion);
3396 }
3397 this.copilot_state.cycle_completions(direction);
3398 this.update_visible_copilot_suggestion(cx);
3399 })
3400 .log_err()?;
3401
3402 Some(())
3403 });
3404 }
3405
3406 Some(())
3407 }
3408
3409 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3410 if !self.has_active_copilot_suggestion(cx) {
3411 self.refresh_copilot_suggestions(false, cx);
3412 return;
3413 }
3414
3415 self.update_visible_copilot_suggestion(cx);
3416 }
3417
3418 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3419 if self.has_active_copilot_suggestion(cx) {
3420 self.cycle_copilot_suggestions(Direction::Next, cx);
3421 } else {
3422 self.refresh_copilot_suggestions(false, cx);
3423 }
3424 }
3425
3426 fn previous_copilot_suggestion(
3427 &mut self,
3428 _: &copilot::PreviousSuggestion,
3429 cx: &mut ViewContext<Self>,
3430 ) {
3431 if self.has_active_copilot_suggestion(cx) {
3432 self.cycle_copilot_suggestions(Direction::Prev, cx);
3433 } else {
3434 self.refresh_copilot_suggestions(false, cx);
3435 }
3436 }
3437
3438 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3439 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3440 if let Some((copilot, completion)) =
3441 Copilot::global(cx).zip(self.copilot_state.active_completion())
3442 {
3443 copilot
3444 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3445 .detach_and_log_err(cx);
3446
3447 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3448 }
3449 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3450 cx.notify();
3451 true
3452 } else {
3453 false
3454 }
3455 }
3456
3457 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3458 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3459 if let Some(copilot) = Copilot::global(cx) {
3460 copilot
3461 .update(cx, |copilot, cx| {
3462 copilot.discard_completions(&self.copilot_state.completions, cx)
3463 })
3464 .detach_and_log_err(cx);
3465
3466 self.report_copilot_event(None, false, cx)
3467 }
3468
3469 self.display_map.update(cx, |map, cx| {
3470 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3471 });
3472 cx.notify();
3473 true
3474 } else {
3475 false
3476 }
3477 }
3478
3479 fn is_copilot_enabled_at(
3480 &self,
3481 location: Anchor,
3482 snapshot: &MultiBufferSnapshot,
3483 cx: &mut ViewContext<Self>,
3484 ) -> bool {
3485 let file = snapshot.file_at(location);
3486 let language = snapshot.language_at(location);
3487 let settings = all_language_settings(file, cx);
3488 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3489 }
3490
3491 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3492 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3493 let buffer = self.buffer.read(cx).read(cx);
3494 suggestion.position.is_valid(&buffer)
3495 } else {
3496 false
3497 }
3498 }
3499
3500 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3501 let suggestion = self.copilot_state.suggestion.take()?;
3502 self.display_map.update(cx, |map, cx| {
3503 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3504 });
3505 let buffer = self.buffer.read(cx).read(cx);
3506
3507 if suggestion.position.is_valid(&buffer) {
3508 Some(suggestion)
3509 } else {
3510 None
3511 }
3512 }
3513
3514 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3515 let snapshot = self.buffer.read(cx).snapshot(cx);
3516 let selection = self.selections.newest_anchor();
3517 let cursor = selection.head();
3518
3519 if self.context_menu.is_some()
3520 || !self.completion_tasks.is_empty()
3521 || selection.start != selection.end
3522 {
3523 self.discard_copilot_suggestion(cx);
3524 } else if let Some(text) = self
3525 .copilot_state
3526 .text_for_active_completion(cursor, &snapshot)
3527 {
3528 let text = Rope::from(text);
3529 let mut to_remove = Vec::new();
3530 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3531 to_remove.push(suggestion.id);
3532 }
3533
3534 let suggestion_inlay =
3535 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3536 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3537 self.display_map.update(cx, move |map, cx| {
3538 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3539 });
3540 cx.notify();
3541 } else {
3542 self.discard_copilot_suggestion(cx);
3543 }
3544 }
3545
3546 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3547 self.copilot_state = Default::default();
3548 self.discard_copilot_suggestion(cx);
3549 }
3550
3551 pub fn render_code_actions_indicator(
3552 &self,
3553 style: &EditorStyle,
3554 is_active: bool,
3555 cx: &mut ViewContext<Self>,
3556 ) -> Option<AnyElement<Self>> {
3557 if self.available_code_actions.is_some() {
3558 enum CodeActions {}
3559 Some(
3560 MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
3561 Svg::new("icons/bolt_8.svg").with_color(
3562 style
3563 .code_actions
3564 .indicator
3565 .in_state(is_active)
3566 .style_for(state)
3567 .color,
3568 )
3569 })
3570 .with_cursor_style(CursorStyle::PointingHand)
3571 .with_padding(Padding::uniform(3.))
3572 .on_down(MouseButton::Left, |_, this, cx| {
3573 this.toggle_code_actions(
3574 &ToggleCodeActions {
3575 deployed_from_indicator: true,
3576 },
3577 cx,
3578 );
3579 })
3580 .into_any(),
3581 )
3582 } else {
3583 None
3584 }
3585 }
3586
3587 pub fn render_fold_indicators(
3588 &self,
3589 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3590 style: &EditorStyle,
3591 gutter_hovered: bool,
3592 line_height: f32,
3593 gutter_margin: f32,
3594 cx: &mut ViewContext<Self>,
3595 ) -> Vec<Option<AnyElement<Self>>> {
3596 enum FoldIndicators {}
3597
3598 let style = style.folds.clone();
3599
3600 fold_data
3601 .iter()
3602 .enumerate()
3603 .map(|(ix, fold_data)| {
3604 fold_data
3605 .map(|(fold_status, buffer_row, active)| {
3606 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3607 MouseEventHandler::new::<FoldIndicators, _>(
3608 ix as usize,
3609 cx,
3610 |mouse_state, _| {
3611 Svg::new(match fold_status {
3612 FoldStatus::Folded => style.folded_icon.clone(),
3613 FoldStatus::Foldable => style.foldable_icon.clone(),
3614 })
3615 .with_color(
3616 style
3617 .indicator
3618 .in_state(fold_status == FoldStatus::Folded)
3619 .style_for(mouse_state)
3620 .color,
3621 )
3622 .constrained()
3623 .with_width(gutter_margin * style.icon_margin_scale)
3624 .aligned()
3625 .constrained()
3626 .with_height(line_height)
3627 .with_width(gutter_margin)
3628 .aligned()
3629 },
3630 )
3631 .with_cursor_style(CursorStyle::PointingHand)
3632 .with_padding(Padding::uniform(3.))
3633 .on_click(MouseButton::Left, {
3634 move |_, editor, cx| match fold_status {
3635 FoldStatus::Folded => {
3636 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3637 }
3638 FoldStatus::Foldable => {
3639 editor.fold_at(&FoldAt { buffer_row }, cx);
3640 }
3641 }
3642 })
3643 .into_any()
3644 })
3645 })
3646 .flatten()
3647 })
3648 .collect()
3649 }
3650
3651 pub fn context_menu_visible(&self) -> bool {
3652 self.context_menu
3653 .as_ref()
3654 .map_or(false, |menu| menu.visible())
3655 }
3656
3657 pub fn render_context_menu(
3658 &self,
3659 cursor_position: DisplayPoint,
3660 style: EditorStyle,
3661 cx: &mut ViewContext<Editor>,
3662 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3663 self.context_menu
3664 .as_ref()
3665 .map(|menu| menu.render(cursor_position, style, cx))
3666 }
3667
3668 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3669 if !matches!(menu, ContextMenu::Completions(_)) {
3670 self.completion_tasks.clear();
3671 }
3672 self.context_menu = Some(menu);
3673 self.discard_copilot_suggestion(cx);
3674 cx.notify();
3675 }
3676
3677 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3678 cx.notify();
3679 self.completion_tasks.clear();
3680 let context_menu = self.context_menu.take();
3681 if context_menu.is_some() {
3682 self.update_visible_copilot_suggestion(cx);
3683 }
3684 context_menu
3685 }
3686
3687 pub fn insert_snippet(
3688 &mut self,
3689 insertion_ranges: &[Range<usize>],
3690 snippet: Snippet,
3691 cx: &mut ViewContext<Self>,
3692 ) -> Result<()> {
3693 let tabstops = self.buffer.update(cx, |buffer, cx| {
3694 let snippet_text: Arc<str> = snippet.text.clone().into();
3695 buffer.edit(
3696 insertion_ranges
3697 .iter()
3698 .cloned()
3699 .map(|range| (range, snippet_text.clone())),
3700 Some(AutoindentMode::EachLine),
3701 cx,
3702 );
3703
3704 let snapshot = &*buffer.read(cx);
3705 let snippet = &snippet;
3706 snippet
3707 .tabstops
3708 .iter()
3709 .map(|tabstop| {
3710 let mut tabstop_ranges = tabstop
3711 .iter()
3712 .flat_map(|tabstop_range| {
3713 let mut delta = 0_isize;
3714 insertion_ranges.iter().map(move |insertion_range| {
3715 let insertion_start = insertion_range.start as isize + delta;
3716 delta +=
3717 snippet.text.len() as isize - insertion_range.len() as isize;
3718
3719 let start = snapshot.anchor_before(
3720 (insertion_start + tabstop_range.start) as usize,
3721 );
3722 let end = snapshot
3723 .anchor_after((insertion_start + tabstop_range.end) as usize);
3724 start..end
3725 })
3726 })
3727 .collect::<Vec<_>>();
3728 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3729 tabstop_ranges
3730 })
3731 .collect::<Vec<_>>()
3732 });
3733
3734 if let Some(tabstop) = tabstops.first() {
3735 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3736 s.select_ranges(tabstop.iter().cloned());
3737 });
3738 self.snippet_stack.push(SnippetState {
3739 active_index: 0,
3740 ranges: tabstops,
3741 });
3742 }
3743
3744 Ok(())
3745 }
3746
3747 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3748 self.move_to_snippet_tabstop(Bias::Right, cx)
3749 }
3750
3751 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3752 self.move_to_snippet_tabstop(Bias::Left, cx)
3753 }
3754
3755 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3756 if let Some(mut snippet) = self.snippet_stack.pop() {
3757 match bias {
3758 Bias::Left => {
3759 if snippet.active_index > 0 {
3760 snippet.active_index -= 1;
3761 } else {
3762 self.snippet_stack.push(snippet);
3763 return false;
3764 }
3765 }
3766 Bias::Right => {
3767 if snippet.active_index + 1 < snippet.ranges.len() {
3768 snippet.active_index += 1;
3769 } else {
3770 self.snippet_stack.push(snippet);
3771 return false;
3772 }
3773 }
3774 }
3775 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3776 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3777 s.select_anchor_ranges(current_ranges.iter().cloned())
3778 });
3779 // If snippet state is not at the last tabstop, push it back on the stack
3780 if snippet.active_index + 1 < snippet.ranges.len() {
3781 self.snippet_stack.push(snippet);
3782 }
3783 return true;
3784 }
3785 }
3786
3787 false
3788 }
3789
3790 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3791 self.transact(cx, |this, cx| {
3792 this.select_all(&SelectAll, cx);
3793 this.insert("", cx);
3794 });
3795 }
3796
3797 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3798 self.transact(cx, |this, cx| {
3799 this.select_autoclose_pair(cx);
3800 let mut selections = this.selections.all::<Point>(cx);
3801 if !this.selections.line_mode {
3802 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3803 for selection in &mut selections {
3804 if selection.is_empty() {
3805 let old_head = selection.head();
3806 let mut new_head =
3807 movement::left(&display_map, old_head.to_display_point(&display_map))
3808 .to_point(&display_map);
3809 if let Some((buffer, line_buffer_range)) = display_map
3810 .buffer_snapshot
3811 .buffer_line_for_row(old_head.row)
3812 {
3813 let indent_size =
3814 buffer.indent_size_for_line(line_buffer_range.start.row);
3815 let indent_len = match indent_size.kind {
3816 IndentKind::Space => {
3817 buffer.settings_at(line_buffer_range.start, cx).tab_size
3818 }
3819 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3820 };
3821 if old_head.column <= indent_size.len && old_head.column > 0 {
3822 let indent_len = indent_len.get();
3823 new_head = cmp::min(
3824 new_head,
3825 Point::new(
3826 old_head.row,
3827 ((old_head.column - 1) / indent_len) * indent_len,
3828 ),
3829 );
3830 }
3831 }
3832
3833 selection.set_head(new_head, SelectionGoal::None);
3834 }
3835 }
3836 }
3837
3838 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3839 this.insert("", cx);
3840 this.refresh_copilot_suggestions(true, cx);
3841 });
3842 }
3843
3844 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3845 self.transact(cx, |this, cx| {
3846 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3847 let line_mode = s.line_mode;
3848 s.move_with(|map, selection| {
3849 if selection.is_empty() && !line_mode {
3850 let cursor = movement::right(map, selection.head());
3851 selection.end = cursor;
3852 selection.reversed = true;
3853 selection.goal = SelectionGoal::None;
3854 }
3855 })
3856 });
3857 this.insert("", cx);
3858 this.refresh_copilot_suggestions(true, cx);
3859 });
3860 }
3861
3862 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3863 if self.move_to_prev_snippet_tabstop(cx) {
3864 return;
3865 }
3866
3867 self.outdent(&Outdent, cx);
3868 }
3869
3870 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3871 if self.move_to_next_snippet_tabstop(cx) {
3872 return;
3873 }
3874
3875 let mut selections = self.selections.all_adjusted(cx);
3876 let buffer = self.buffer.read(cx);
3877 let snapshot = buffer.snapshot(cx);
3878 let rows_iter = selections.iter().map(|s| s.head().row);
3879 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3880
3881 let mut edits = Vec::new();
3882 let mut prev_edited_row = 0;
3883 let mut row_delta = 0;
3884 for selection in &mut selections {
3885 if selection.start.row != prev_edited_row {
3886 row_delta = 0;
3887 }
3888 prev_edited_row = selection.end.row;
3889
3890 // If the selection is non-empty, then increase the indentation of the selected lines.
3891 if !selection.is_empty() {
3892 row_delta =
3893 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3894 continue;
3895 }
3896
3897 // If the selection is empty and the cursor is in the leading whitespace before the
3898 // suggested indentation, then auto-indent the line.
3899 let cursor = selection.head();
3900 let current_indent = snapshot.indent_size_for_line(cursor.row);
3901 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3902 if cursor.column < suggested_indent.len
3903 && cursor.column <= current_indent.len
3904 && current_indent.len <= suggested_indent.len
3905 {
3906 selection.start = Point::new(cursor.row, suggested_indent.len);
3907 selection.end = selection.start;
3908 if row_delta == 0 {
3909 edits.extend(Buffer::edit_for_indent_size_adjustment(
3910 cursor.row,
3911 current_indent,
3912 suggested_indent,
3913 ));
3914 row_delta = suggested_indent.len - current_indent.len;
3915 }
3916 continue;
3917 }
3918 }
3919
3920 // Accept copilot suggestion if there is only one selection and the cursor is not
3921 // in the leading whitespace.
3922 if self.selections.count() == 1
3923 && cursor.column >= current_indent.len
3924 && self.has_active_copilot_suggestion(cx)
3925 {
3926 self.accept_copilot_suggestion(cx);
3927 return;
3928 }
3929
3930 // Otherwise, insert a hard or soft tab.
3931 let settings = buffer.settings_at(cursor, cx);
3932 let tab_size = if settings.hard_tabs {
3933 IndentSize::tab()
3934 } else {
3935 let tab_size = settings.tab_size.get();
3936 let char_column = snapshot
3937 .text_for_range(Point::new(cursor.row, 0)..cursor)
3938 .flat_map(str::chars)
3939 .count()
3940 + row_delta as usize;
3941 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3942 IndentSize::spaces(chars_to_next_tab_stop)
3943 };
3944 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3945 selection.end = selection.start;
3946 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3947 row_delta += tab_size.len;
3948 }
3949
3950 self.transact(cx, |this, cx| {
3951 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3952 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3953 this.refresh_copilot_suggestions(true, cx);
3954 });
3955 }
3956
3957 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3958 let mut selections = self.selections.all::<Point>(cx);
3959 let mut prev_edited_row = 0;
3960 let mut row_delta = 0;
3961 let mut edits = Vec::new();
3962 let buffer = self.buffer.read(cx);
3963 let snapshot = buffer.snapshot(cx);
3964 for selection in &mut selections {
3965 if selection.start.row != prev_edited_row {
3966 row_delta = 0;
3967 }
3968 prev_edited_row = selection.end.row;
3969
3970 row_delta =
3971 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3972 }
3973
3974 self.transact(cx, |this, cx| {
3975 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3976 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3977 });
3978 }
3979
3980 fn indent_selection(
3981 buffer: &MultiBuffer,
3982 snapshot: &MultiBufferSnapshot,
3983 selection: &mut Selection<Point>,
3984 edits: &mut Vec<(Range<Point>, String)>,
3985 delta_for_start_row: u32,
3986 cx: &AppContext,
3987 ) -> u32 {
3988 let settings = buffer.settings_at(selection.start, cx);
3989 let tab_size = settings.tab_size.get();
3990 let indent_kind = if settings.hard_tabs {
3991 IndentKind::Tab
3992 } else {
3993 IndentKind::Space
3994 };
3995 let mut start_row = selection.start.row;
3996 let mut end_row = selection.end.row + 1;
3997
3998 // If a selection ends at the beginning of a line, don't indent
3999 // that last line.
4000 if selection.end.column == 0 {
4001 end_row -= 1;
4002 }
4003
4004 // Avoid re-indenting a row that has already been indented by a
4005 // previous selection, but still update this selection's column
4006 // to reflect that indentation.
4007 if delta_for_start_row > 0 {
4008 start_row += 1;
4009 selection.start.column += delta_for_start_row;
4010 if selection.end.row == selection.start.row {
4011 selection.end.column += delta_for_start_row;
4012 }
4013 }
4014
4015 let mut delta_for_end_row = 0;
4016 for row in start_row..end_row {
4017 let current_indent = snapshot.indent_size_for_line(row);
4018 let indent_delta = match (current_indent.kind, indent_kind) {
4019 (IndentKind::Space, IndentKind::Space) => {
4020 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4021 IndentSize::spaces(columns_to_next_tab_stop)
4022 }
4023 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4024 (_, IndentKind::Tab) => IndentSize::tab(),
4025 };
4026
4027 let row_start = Point::new(row, 0);
4028 edits.push((
4029 row_start..row_start,
4030 indent_delta.chars().collect::<String>(),
4031 ));
4032
4033 // Update this selection's endpoints to reflect the indentation.
4034 if row == selection.start.row {
4035 selection.start.column += indent_delta.len;
4036 }
4037 if row == selection.end.row {
4038 selection.end.column += indent_delta.len;
4039 delta_for_end_row = indent_delta.len;
4040 }
4041 }
4042
4043 if selection.start.row == selection.end.row {
4044 delta_for_start_row + delta_for_end_row
4045 } else {
4046 delta_for_end_row
4047 }
4048 }
4049
4050 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4051 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4052 let selections = self.selections.all::<Point>(cx);
4053 let mut deletion_ranges = Vec::new();
4054 let mut last_outdent = None;
4055 {
4056 let buffer = self.buffer.read(cx);
4057 let snapshot = buffer.snapshot(cx);
4058 for selection in &selections {
4059 let settings = buffer.settings_at(selection.start, cx);
4060 let tab_size = settings.tab_size.get();
4061 let mut rows = selection.spanned_rows(false, &display_map);
4062
4063 // Avoid re-outdenting a row that has already been outdented by a
4064 // previous selection.
4065 if let Some(last_row) = last_outdent {
4066 if last_row == rows.start {
4067 rows.start += 1;
4068 }
4069 }
4070
4071 for row in rows {
4072 let indent_size = snapshot.indent_size_for_line(row);
4073 if indent_size.len > 0 {
4074 let deletion_len = match indent_size.kind {
4075 IndentKind::Space => {
4076 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4077 if columns_to_prev_tab_stop == 0 {
4078 tab_size
4079 } else {
4080 columns_to_prev_tab_stop
4081 }
4082 }
4083 IndentKind::Tab => 1,
4084 };
4085 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4086 last_outdent = Some(row);
4087 }
4088 }
4089 }
4090 }
4091
4092 self.transact(cx, |this, cx| {
4093 this.buffer.update(cx, |buffer, cx| {
4094 let empty_str: Arc<str> = "".into();
4095 buffer.edit(
4096 deletion_ranges
4097 .into_iter()
4098 .map(|range| (range, empty_str.clone())),
4099 None,
4100 cx,
4101 );
4102 });
4103 let selections = this.selections.all::<usize>(cx);
4104 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4105 });
4106 }
4107
4108 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4109 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4110 let selections = self.selections.all::<Point>(cx);
4111
4112 let mut new_cursors = Vec::new();
4113 let mut edit_ranges = Vec::new();
4114 let mut selections = selections.iter().peekable();
4115 while let Some(selection) = selections.next() {
4116 let mut rows = selection.spanned_rows(false, &display_map);
4117 let goal_display_column = selection.head().to_display_point(&display_map).column();
4118
4119 // Accumulate contiguous regions of rows that we want to delete.
4120 while let Some(next_selection) = selections.peek() {
4121 let next_rows = next_selection.spanned_rows(false, &display_map);
4122 if next_rows.start <= rows.end {
4123 rows.end = next_rows.end;
4124 selections.next().unwrap();
4125 } else {
4126 break;
4127 }
4128 }
4129
4130 let buffer = &display_map.buffer_snapshot;
4131 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4132 let edit_end;
4133 let cursor_buffer_row;
4134 if buffer.max_point().row >= rows.end {
4135 // If there's a line after the range, delete the \n from the end of the row range
4136 // and position the cursor on the next line.
4137 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4138 cursor_buffer_row = rows.end;
4139 } else {
4140 // If there isn't a line after the range, delete the \n from the line before the
4141 // start of the row range and position the cursor there.
4142 edit_start = edit_start.saturating_sub(1);
4143 edit_end = buffer.len();
4144 cursor_buffer_row = rows.start.saturating_sub(1);
4145 }
4146
4147 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4148 *cursor.column_mut() =
4149 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4150
4151 new_cursors.push((
4152 selection.id,
4153 buffer.anchor_after(cursor.to_point(&display_map)),
4154 ));
4155 edit_ranges.push(edit_start..edit_end);
4156 }
4157
4158 self.transact(cx, |this, cx| {
4159 let buffer = this.buffer.update(cx, |buffer, cx| {
4160 let empty_str: Arc<str> = "".into();
4161 buffer.edit(
4162 edit_ranges
4163 .into_iter()
4164 .map(|range| (range, empty_str.clone())),
4165 None,
4166 cx,
4167 );
4168 buffer.snapshot(cx)
4169 });
4170 let new_selections = new_cursors
4171 .into_iter()
4172 .map(|(id, cursor)| {
4173 let cursor = cursor.to_point(&buffer);
4174 Selection {
4175 id,
4176 start: cursor,
4177 end: cursor,
4178 reversed: false,
4179 goal: SelectionGoal::None,
4180 }
4181 })
4182 .collect();
4183
4184 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4185 s.select(new_selections);
4186 });
4187 });
4188 }
4189
4190 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4191 let mut row_ranges = Vec::<Range<u32>>::new();
4192 for selection in self.selections.all::<Point>(cx) {
4193 let start = selection.start.row;
4194 let end = if selection.start.row == selection.end.row {
4195 selection.start.row + 1
4196 } else {
4197 selection.end.row
4198 };
4199
4200 if let Some(last_row_range) = row_ranges.last_mut() {
4201 if start <= last_row_range.end {
4202 last_row_range.end = end;
4203 continue;
4204 }
4205 }
4206 row_ranges.push(start..end);
4207 }
4208
4209 let snapshot = self.buffer.read(cx).snapshot(cx);
4210 let mut cursor_positions = Vec::new();
4211 for row_range in &row_ranges {
4212 let anchor = snapshot.anchor_before(Point::new(
4213 row_range.end - 1,
4214 snapshot.line_len(row_range.end - 1),
4215 ));
4216 cursor_positions.push(anchor.clone()..anchor);
4217 }
4218
4219 self.transact(cx, |this, cx| {
4220 for row_range in row_ranges.into_iter().rev() {
4221 for row in row_range.rev() {
4222 let end_of_line = Point::new(row, snapshot.line_len(row));
4223 let indent = snapshot.indent_size_for_line(row + 1);
4224 let start_of_next_line = Point::new(row + 1, indent.len);
4225
4226 let replace = if snapshot.line_len(row + 1) > indent.len {
4227 " "
4228 } else {
4229 ""
4230 };
4231
4232 this.buffer.update(cx, |buffer, cx| {
4233 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4234 });
4235 }
4236 }
4237
4238 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4239 s.select_anchor_ranges(cursor_positions)
4240 });
4241 });
4242 }
4243
4244 pub fn sort_lines_case_sensitive(
4245 &mut self,
4246 _: &SortLinesCaseSensitive,
4247 cx: &mut ViewContext<Self>,
4248 ) {
4249 self.manipulate_lines(cx, |lines| lines.sort())
4250 }
4251
4252 pub fn sort_lines_case_insensitive(
4253 &mut self,
4254 _: &SortLinesCaseInsensitive,
4255 cx: &mut ViewContext<Self>,
4256 ) {
4257 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4258 }
4259
4260 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4261 self.manipulate_lines(cx, |lines| lines.reverse())
4262 }
4263
4264 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4265 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4266 }
4267
4268 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4269 where
4270 Fn: FnMut(&mut [&str]),
4271 {
4272 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4273 let buffer = self.buffer.read(cx).snapshot(cx);
4274
4275 let mut edits = Vec::new();
4276
4277 let selections = self.selections.all::<Point>(cx);
4278 let mut selections = selections.iter().peekable();
4279 let mut contiguous_row_selections = Vec::new();
4280 let mut new_selections = Vec::new();
4281
4282 while let Some(selection) = selections.next() {
4283 let (start_row, end_row) = consume_contiguous_rows(
4284 &mut contiguous_row_selections,
4285 selection,
4286 &display_map,
4287 &mut selections,
4288 );
4289
4290 let start_point = Point::new(start_row, 0);
4291 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4292 let text = buffer
4293 .text_for_range(start_point..end_point)
4294 .collect::<String>();
4295 let mut lines = text.split("\n").collect_vec();
4296
4297 let lines_len = lines.len();
4298 callback(&mut lines);
4299
4300 // This is a current limitation with selections.
4301 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4302 debug_assert!(
4303 lines.len() == lines_len,
4304 "callback should not change the number of lines"
4305 );
4306
4307 edits.push((start_point..end_point, lines.join("\n")));
4308 let start_anchor = buffer.anchor_after(start_point);
4309 let end_anchor = buffer.anchor_before(end_point);
4310
4311 // Make selection and push
4312 new_selections.push(Selection {
4313 id: selection.id,
4314 start: start_anchor.to_offset(&buffer),
4315 end: end_anchor.to_offset(&buffer),
4316 goal: SelectionGoal::None,
4317 reversed: selection.reversed,
4318 });
4319 }
4320
4321 self.transact(cx, |this, cx| {
4322 this.buffer.update(cx, |buffer, cx| {
4323 buffer.edit(edits, None, cx);
4324 });
4325
4326 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4327 s.select(new_selections);
4328 });
4329
4330 this.request_autoscroll(Autoscroll::fit(), cx);
4331 });
4332 }
4333
4334 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4335 self.manipulate_text(cx, |text| text.to_uppercase())
4336 }
4337
4338 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4339 self.manipulate_text(cx, |text| text.to_lowercase())
4340 }
4341
4342 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4343 self.manipulate_text(cx, |text| text.to_case(Case::Title))
4344 }
4345
4346 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4347 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4348 }
4349
4350 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4351 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4352 }
4353
4354 pub fn convert_to_upper_camel_case(
4355 &mut self,
4356 _: &ConvertToUpperCamelCase,
4357 cx: &mut ViewContext<Self>,
4358 ) {
4359 self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel))
4360 }
4361
4362 pub fn convert_to_lower_camel_case(
4363 &mut self,
4364 _: &ConvertToLowerCamelCase,
4365 cx: &mut ViewContext<Self>,
4366 ) {
4367 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4368 }
4369
4370 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4371 where
4372 Fn: FnMut(&str) -> String,
4373 {
4374 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4375 let buffer = self.buffer.read(cx).snapshot(cx);
4376
4377 let mut new_selections = Vec::new();
4378 let mut edits = Vec::new();
4379 let mut selection_adjustment = 0i32;
4380
4381 for selection in self.selections.all::<usize>(cx) {
4382 let selection_is_empty = selection.is_empty();
4383
4384 let (start, end) = if selection_is_empty {
4385 let word_range = movement::surrounding_word(
4386 &display_map,
4387 selection.start.to_display_point(&display_map),
4388 );
4389 let start = word_range.start.to_offset(&display_map, Bias::Left);
4390 let end = word_range.end.to_offset(&display_map, Bias::Left);
4391 (start, end)
4392 } else {
4393 (selection.start, selection.end)
4394 };
4395
4396 let text = buffer.text_for_range(start..end).collect::<String>();
4397 let old_length = text.len() as i32;
4398 let text = callback(&text);
4399
4400 new_selections.push(Selection {
4401 start: (start as i32 - selection_adjustment) as usize,
4402 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
4403 goal: SelectionGoal::None,
4404 ..selection
4405 });
4406
4407 selection_adjustment += old_length - text.len() as i32;
4408
4409 edits.push((start..end, text));
4410 }
4411
4412 self.transact(cx, |this, cx| {
4413 this.buffer.update(cx, |buffer, cx| {
4414 buffer.edit(edits, None, cx);
4415 });
4416
4417 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4418 s.select(new_selections);
4419 });
4420
4421 this.request_autoscroll(Autoscroll::fit(), cx);
4422 });
4423 }
4424
4425 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
4426 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4427 let buffer = &display_map.buffer_snapshot;
4428 let selections = self.selections.all::<Point>(cx);
4429
4430 let mut edits = Vec::new();
4431 let mut selections_iter = selections.iter().peekable();
4432 while let Some(selection) = selections_iter.next() {
4433 // Avoid duplicating the same lines twice.
4434 let mut rows = selection.spanned_rows(false, &display_map);
4435
4436 while let Some(next_selection) = selections_iter.peek() {
4437 let next_rows = next_selection.spanned_rows(false, &display_map);
4438 if next_rows.start < rows.end {
4439 rows.end = next_rows.end;
4440 selections_iter.next().unwrap();
4441 } else {
4442 break;
4443 }
4444 }
4445
4446 // Copy the text from the selected row region and splice it at the start of the region.
4447 let start = Point::new(rows.start, 0);
4448 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
4449 let text = buffer
4450 .text_for_range(start..end)
4451 .chain(Some("\n"))
4452 .collect::<String>();
4453 edits.push((start..start, text));
4454 }
4455
4456 self.transact(cx, |this, cx| {
4457 this.buffer.update(cx, |buffer, cx| {
4458 buffer.edit(edits, None, cx);
4459 });
4460
4461 this.request_autoscroll(Autoscroll::fit(), cx);
4462 });
4463 }
4464
4465 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
4466 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4467 let buffer = self.buffer.read(cx).snapshot(cx);
4468
4469 let mut edits = Vec::new();
4470 let mut unfold_ranges = Vec::new();
4471 let mut refold_ranges = Vec::new();
4472
4473 let selections = self.selections.all::<Point>(cx);
4474 let mut selections = selections.iter().peekable();
4475 let mut contiguous_row_selections = Vec::new();
4476 let mut new_selections = Vec::new();
4477
4478 while let Some(selection) = selections.next() {
4479 // Find all the selections that span a contiguous row range
4480 let (start_row, end_row) = consume_contiguous_rows(
4481 &mut contiguous_row_selections,
4482 selection,
4483 &display_map,
4484 &mut selections,
4485 );
4486
4487 // Move the text spanned by the row range to be before the line preceding the row range
4488 if start_row > 0 {
4489 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
4490 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
4491 let insertion_point = display_map
4492 .prev_line_boundary(Point::new(start_row - 1, 0))
4493 .0;
4494
4495 // Don't move lines across excerpts
4496 if buffer
4497 .excerpt_boundaries_in_range((
4498 Bound::Excluded(insertion_point),
4499 Bound::Included(range_to_move.end),
4500 ))
4501 .next()
4502 .is_none()
4503 {
4504 let text = buffer
4505 .text_for_range(range_to_move.clone())
4506 .flat_map(|s| s.chars())
4507 .skip(1)
4508 .chain(['\n'])
4509 .collect::<String>();
4510
4511 edits.push((
4512 buffer.anchor_after(range_to_move.start)
4513 ..buffer.anchor_before(range_to_move.end),
4514 String::new(),
4515 ));
4516 let insertion_anchor = buffer.anchor_after(insertion_point);
4517 edits.push((insertion_anchor..insertion_anchor, text));
4518
4519 let row_delta = range_to_move.start.row - insertion_point.row + 1;
4520
4521 // Move selections up
4522 new_selections.extend(contiguous_row_selections.drain(..).map(
4523 |mut selection| {
4524 selection.start.row -= row_delta;
4525 selection.end.row -= row_delta;
4526 selection
4527 },
4528 ));
4529
4530 // Move folds up
4531 unfold_ranges.push(range_to_move.clone());
4532 for fold in display_map.folds_in_range(
4533 buffer.anchor_before(range_to_move.start)
4534 ..buffer.anchor_after(range_to_move.end),
4535 ) {
4536 let mut start = fold.start.to_point(&buffer);
4537 let mut end = fold.end.to_point(&buffer);
4538 start.row -= row_delta;
4539 end.row -= row_delta;
4540 refold_ranges.push(start..end);
4541 }
4542 }
4543 }
4544
4545 // If we didn't move line(s), preserve the existing selections
4546 new_selections.append(&mut contiguous_row_selections);
4547 }
4548
4549 self.transact(cx, |this, cx| {
4550 this.unfold_ranges(unfold_ranges, true, true, cx);
4551 this.buffer.update(cx, |buffer, cx| {
4552 for (range, text) in edits {
4553 buffer.edit([(range, text)], None, cx);
4554 }
4555 });
4556 this.fold_ranges(refold_ranges, true, cx);
4557 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4558 s.select(new_selections);
4559 })
4560 });
4561 }
4562
4563 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
4564 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4565 let buffer = self.buffer.read(cx).snapshot(cx);
4566
4567 let mut edits = Vec::new();
4568 let mut unfold_ranges = Vec::new();
4569 let mut refold_ranges = Vec::new();
4570
4571 let selections = self.selections.all::<Point>(cx);
4572 let mut selections = selections.iter().peekable();
4573 let mut contiguous_row_selections = Vec::new();
4574 let mut new_selections = Vec::new();
4575
4576 while let Some(selection) = selections.next() {
4577 // Find all the selections that span a contiguous row range
4578 let (start_row, end_row) = consume_contiguous_rows(
4579 &mut contiguous_row_selections,
4580 selection,
4581 &display_map,
4582 &mut selections,
4583 );
4584
4585 // Move the text spanned by the row range to be after the last line of the row range
4586 if end_row <= buffer.max_point().row {
4587 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
4588 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
4589
4590 // Don't move lines across excerpt boundaries
4591 if buffer
4592 .excerpt_boundaries_in_range((
4593 Bound::Excluded(range_to_move.start),
4594 Bound::Included(insertion_point),
4595 ))
4596 .next()
4597 .is_none()
4598 {
4599 let mut text = String::from("\n");
4600 text.extend(buffer.text_for_range(range_to_move.clone()));
4601 text.pop(); // Drop trailing newline
4602 edits.push((
4603 buffer.anchor_after(range_to_move.start)
4604 ..buffer.anchor_before(range_to_move.end),
4605 String::new(),
4606 ));
4607 let insertion_anchor = buffer.anchor_after(insertion_point);
4608 edits.push((insertion_anchor..insertion_anchor, text));
4609
4610 let row_delta = insertion_point.row - range_to_move.end.row + 1;
4611
4612 // Move selections down
4613 new_selections.extend(contiguous_row_selections.drain(..).map(
4614 |mut selection| {
4615 selection.start.row += row_delta;
4616 selection.end.row += row_delta;
4617 selection
4618 },
4619 ));
4620
4621 // Move folds down
4622 unfold_ranges.push(range_to_move.clone());
4623 for fold in display_map.folds_in_range(
4624 buffer.anchor_before(range_to_move.start)
4625 ..buffer.anchor_after(range_to_move.end),
4626 ) {
4627 let mut start = fold.start.to_point(&buffer);
4628 let mut end = fold.end.to_point(&buffer);
4629 start.row += row_delta;
4630 end.row += row_delta;
4631 refold_ranges.push(start..end);
4632 }
4633 }
4634 }
4635
4636 // If we didn't move line(s), preserve the existing selections
4637 new_selections.append(&mut contiguous_row_selections);
4638 }
4639
4640 self.transact(cx, |this, cx| {
4641 this.unfold_ranges(unfold_ranges, true, true, cx);
4642 this.buffer.update(cx, |buffer, cx| {
4643 for (range, text) in edits {
4644 buffer.edit([(range, text)], None, cx);
4645 }
4646 });
4647 this.fold_ranges(refold_ranges, true, cx);
4648 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
4649 });
4650 }
4651
4652 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
4653 self.transact(cx, |this, cx| {
4654 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4655 let mut edits: Vec<(Range<usize>, String)> = Default::default();
4656 let line_mode = s.line_mode;
4657 s.move_with(|display_map, selection| {
4658 if !selection.is_empty() || line_mode {
4659 return;
4660 }
4661
4662 let mut head = selection.head();
4663 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
4664 if head.column() == display_map.line_len(head.row()) {
4665 transpose_offset = display_map
4666 .buffer_snapshot
4667 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4668 }
4669
4670 if transpose_offset == 0 {
4671 return;
4672 }
4673
4674 *head.column_mut() += 1;
4675 head = display_map.clip_point(head, Bias::Right);
4676 selection.collapse_to(head, SelectionGoal::Column(head.column()));
4677
4678 let transpose_start = display_map
4679 .buffer_snapshot
4680 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
4681 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
4682 let transpose_end = display_map
4683 .buffer_snapshot
4684 .clip_offset(transpose_offset + 1, Bias::Right);
4685 if let Some(ch) =
4686 display_map.buffer_snapshot.chars_at(transpose_start).next()
4687 {
4688 edits.push((transpose_start..transpose_offset, String::new()));
4689 edits.push((transpose_end..transpose_end, ch.to_string()));
4690 }
4691 }
4692 });
4693 edits
4694 });
4695 this.buffer
4696 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
4697 let selections = this.selections.all::<usize>(cx);
4698 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4699 s.select(selections);
4700 });
4701 });
4702 }
4703
4704 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
4705 let mut text = String::new();
4706 let buffer = self.buffer.read(cx).snapshot(cx);
4707 let mut selections = self.selections.all::<Point>(cx);
4708 let mut clipboard_selections = Vec::with_capacity(selections.len());
4709 {
4710 let max_point = buffer.max_point();
4711 for selection in &mut selections {
4712 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4713 if is_entire_line {
4714 selection.start = Point::new(selection.start.row, 0);
4715 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
4716 selection.goal = SelectionGoal::None;
4717 }
4718 let mut len = 0;
4719 for chunk in buffer.text_for_range(selection.start..selection.end) {
4720 text.push_str(chunk);
4721 len += chunk.len();
4722 }
4723 clipboard_selections.push(ClipboardSelection {
4724 len,
4725 is_entire_line,
4726 first_line_indent: buffer.indent_size_for_line(selection.start.row).len,
4727 });
4728 }
4729 }
4730
4731 self.transact(cx, |this, cx| {
4732 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4733 s.select(selections);
4734 });
4735 this.insert("", cx);
4736 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4737 });
4738 }
4739
4740 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
4741 let selections = self.selections.all::<Point>(cx);
4742 let buffer = self.buffer.read(cx).read(cx);
4743 let mut text = String::new();
4744
4745 let mut clipboard_selections = Vec::with_capacity(selections.len());
4746 {
4747 let max_point = buffer.max_point();
4748 for selection in selections.iter() {
4749 let mut start = selection.start;
4750 let mut end = selection.end;
4751 let is_entire_line = selection.is_empty() || self.selections.line_mode;
4752 if is_entire_line {
4753 start = Point::new(start.row, 0);
4754 end = cmp::min(max_point, Point::new(end.row + 1, 0));
4755 }
4756 let mut len = 0;
4757 for chunk in buffer.text_for_range(start..end) {
4758 text.push_str(chunk);
4759 len += chunk.len();
4760 }
4761 clipboard_selections.push(ClipboardSelection {
4762 len,
4763 is_entire_line,
4764 first_line_indent: buffer.indent_size_for_line(start.row).len,
4765 });
4766 }
4767 }
4768
4769 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
4770 }
4771
4772 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
4773 self.transact(cx, |this, cx| {
4774 if let Some(item) = cx.read_from_clipboard() {
4775 let mut clipboard_text = Cow::Borrowed(item.text());
4776 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
4777 let old_selections = this.selections.all::<usize>(cx);
4778 let all_selections_were_entire_line =
4779 clipboard_selections.iter().all(|s| s.is_entire_line);
4780 let first_selection_indent_column =
4781 clipboard_selections.first().map(|s| s.first_line_indent);
4782 if clipboard_selections.len() != old_selections.len() {
4783 let mut newline_separated_text = String::new();
4784 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
4785 let mut ix = 0;
4786 while let Some(clipboard_selection) = clipboard_selections.next() {
4787 newline_separated_text
4788 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
4789 ix += clipboard_selection.len;
4790 if clipboard_selections.peek().is_some() {
4791 newline_separated_text.push('\n');
4792 }
4793 }
4794 clipboard_text = Cow::Owned(newline_separated_text);
4795 }
4796
4797 this.buffer.update(cx, |buffer, cx| {
4798 let snapshot = buffer.read(cx);
4799 let mut start_offset = 0;
4800 let mut edits = Vec::new();
4801 let mut original_indent_columns = Vec::new();
4802 let line_mode = this.selections.line_mode;
4803 for (ix, selection) in old_selections.iter().enumerate() {
4804 let to_insert;
4805 let entire_line;
4806 let original_indent_column;
4807 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
4808 let end_offset = start_offset + clipboard_selection.len;
4809 to_insert = &clipboard_text[start_offset..end_offset];
4810 entire_line = clipboard_selection.is_entire_line;
4811 start_offset = end_offset;
4812 original_indent_column =
4813 Some(clipboard_selection.first_line_indent);
4814 } else {
4815 to_insert = clipboard_text.as_str();
4816 entire_line = all_selections_were_entire_line;
4817 original_indent_column = first_selection_indent_column
4818 }
4819
4820 // If the corresponding selection was empty when this slice of the
4821 // clipboard text was written, then the entire line containing the
4822 // selection was copied. If this selection is also currently empty,
4823 // then paste the line before the current line of the buffer.
4824 let range = if selection.is_empty() && !line_mode && entire_line {
4825 let column = selection.start.to_point(&snapshot).column as usize;
4826 let line_start = selection.start - column;
4827 line_start..line_start
4828 } else {
4829 selection.range()
4830 };
4831
4832 edits.push((range, to_insert));
4833 original_indent_columns.extend(original_indent_column);
4834 }
4835 drop(snapshot);
4836
4837 buffer.edit(
4838 edits,
4839 Some(AutoindentMode::Block {
4840 original_indent_columns,
4841 }),
4842 cx,
4843 );
4844 });
4845
4846 let selections = this.selections.all::<usize>(cx);
4847 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4848 } else {
4849 this.insert(&clipboard_text, cx);
4850 }
4851 }
4852 });
4853 }
4854
4855 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
4856 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
4857 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
4858 self.change_selections(None, cx, |s| {
4859 s.select_anchors(selections.to_vec());
4860 });
4861 }
4862 self.request_autoscroll(Autoscroll::fit(), cx);
4863 self.unmark_text(cx);
4864 self.refresh_copilot_suggestions(true, cx);
4865 cx.emit(Event::Edited);
4866 }
4867 }
4868
4869 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
4870 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
4871 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
4872 {
4873 self.change_selections(None, cx, |s| {
4874 s.select_anchors(selections.to_vec());
4875 });
4876 }
4877 self.request_autoscroll(Autoscroll::fit(), cx);
4878 self.unmark_text(cx);
4879 self.refresh_copilot_suggestions(true, cx);
4880 cx.emit(Event::Edited);
4881 }
4882 }
4883
4884 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
4885 self.buffer
4886 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
4887 }
4888
4889 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
4890 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4891 let line_mode = s.line_mode;
4892 s.move_with(|map, selection| {
4893 let cursor = if selection.is_empty() && !line_mode {
4894 movement::left(map, selection.start)
4895 } else {
4896 selection.start
4897 };
4898 selection.collapse_to(cursor, SelectionGoal::None);
4899 });
4900 })
4901 }
4902
4903 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
4904 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4905 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
4906 })
4907 }
4908
4909 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
4910 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4911 let line_mode = s.line_mode;
4912 s.move_with(|map, selection| {
4913 let cursor = if selection.is_empty() && !line_mode {
4914 movement::right(map, selection.end)
4915 } else {
4916 selection.end
4917 };
4918 selection.collapse_to(cursor, SelectionGoal::None)
4919 });
4920 })
4921 }
4922
4923 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
4924 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4925 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
4926 })
4927 }
4928
4929 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
4930 if self.take_rename(true, cx).is_some() {
4931 return;
4932 }
4933
4934 if let Some(context_menu) = self.context_menu.as_mut() {
4935 if context_menu.select_prev(cx) {
4936 return;
4937 }
4938 }
4939
4940 if matches!(self.mode, EditorMode::SingleLine) {
4941 cx.propagate_action();
4942 return;
4943 }
4944
4945 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4946 let line_mode = s.line_mode;
4947 s.move_with(|map, selection| {
4948 if !selection.is_empty() && !line_mode {
4949 selection.goal = SelectionGoal::None;
4950 }
4951 let (cursor, goal) = movement::up(map, selection.start, selection.goal, false);
4952 selection.collapse_to(cursor, goal);
4953 });
4954 })
4955 }
4956
4957 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
4958 if self.take_rename(true, cx).is_some() {
4959 return;
4960 }
4961
4962 if self
4963 .context_menu
4964 .as_mut()
4965 .map(|menu| menu.select_first(cx))
4966 .unwrap_or(false)
4967 {
4968 return;
4969 }
4970
4971 if matches!(self.mode, EditorMode::SingleLine) {
4972 cx.propagate_action();
4973 return;
4974 }
4975
4976 let row_count = if let Some(row_count) = self.visible_line_count() {
4977 row_count as u32 - 1
4978 } else {
4979 return;
4980 };
4981
4982 let autoscroll = if action.center_cursor {
4983 Autoscroll::center()
4984 } else {
4985 Autoscroll::fit()
4986 };
4987
4988 self.change_selections(Some(autoscroll), cx, |s| {
4989 let line_mode = s.line_mode;
4990 s.move_with(|map, selection| {
4991 if !selection.is_empty() && !line_mode {
4992 selection.goal = SelectionGoal::None;
4993 }
4994 let (cursor, goal) =
4995 movement::up_by_rows(map, selection.end, row_count, selection.goal, false);
4996 selection.collapse_to(cursor, goal);
4997 });
4998 });
4999 }
5000
5001 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
5002 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5003 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
5004 })
5005 }
5006
5007 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
5008 self.take_rename(true, cx);
5009
5010 if let Some(context_menu) = self.context_menu.as_mut() {
5011 if context_menu.select_next(cx) {
5012 return;
5013 }
5014 }
5015
5016 if self.mode == EditorMode::SingleLine {
5017 cx.propagate_action();
5018 return;
5019 }
5020
5021 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5022 let line_mode = s.line_mode;
5023 s.move_with(|map, selection| {
5024 if !selection.is_empty() && !line_mode {
5025 selection.goal = SelectionGoal::None;
5026 }
5027 let (cursor, goal) = movement::down(map, selection.end, selection.goal, false);
5028 selection.collapse_to(cursor, goal);
5029 });
5030 });
5031 }
5032
5033 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
5034 if self.take_rename(true, cx).is_some() {
5035 return;
5036 }
5037
5038 if self
5039 .context_menu
5040 .as_mut()
5041 .map(|menu| menu.select_last(cx))
5042 .unwrap_or(false)
5043 {
5044 return;
5045 }
5046
5047 if matches!(self.mode, EditorMode::SingleLine) {
5048 cx.propagate_action();
5049 return;
5050 }
5051
5052 let row_count = if let Some(row_count) = self.visible_line_count() {
5053 row_count as u32 - 1
5054 } else {
5055 return;
5056 };
5057
5058 let autoscroll = if action.center_cursor {
5059 Autoscroll::center()
5060 } else {
5061 Autoscroll::fit()
5062 };
5063
5064 self.change_selections(Some(autoscroll), cx, |s| {
5065 let line_mode = s.line_mode;
5066 s.move_with(|map, selection| {
5067 if !selection.is_empty() && !line_mode {
5068 selection.goal = SelectionGoal::None;
5069 }
5070 let (cursor, goal) =
5071 movement::down_by_rows(map, selection.end, row_count, selection.goal, false);
5072 selection.collapse_to(cursor, goal);
5073 });
5074 });
5075 }
5076
5077 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
5078 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5079 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
5080 });
5081 }
5082
5083 pub fn move_to_previous_word_start(
5084 &mut self,
5085 _: &MoveToPreviousWordStart,
5086 cx: &mut ViewContext<Self>,
5087 ) {
5088 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5089 s.move_cursors_with(|map, head, _| {
5090 (
5091 movement::previous_word_start(map, head),
5092 SelectionGoal::None,
5093 )
5094 });
5095 })
5096 }
5097
5098 pub fn move_to_previous_subword_start(
5099 &mut self,
5100 _: &MoveToPreviousSubwordStart,
5101 cx: &mut ViewContext<Self>,
5102 ) {
5103 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5104 s.move_cursors_with(|map, head, _| {
5105 (
5106 movement::previous_subword_start(map, head),
5107 SelectionGoal::None,
5108 )
5109 });
5110 })
5111 }
5112
5113 pub fn select_to_previous_word_start(
5114 &mut self,
5115 _: &SelectToPreviousWordStart,
5116 cx: &mut ViewContext<Self>,
5117 ) {
5118 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5119 s.move_heads_with(|map, head, _| {
5120 (
5121 movement::previous_word_start(map, head),
5122 SelectionGoal::None,
5123 )
5124 });
5125 })
5126 }
5127
5128 pub fn select_to_previous_subword_start(
5129 &mut self,
5130 _: &SelectToPreviousSubwordStart,
5131 cx: &mut ViewContext<Self>,
5132 ) {
5133 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5134 s.move_heads_with(|map, head, _| {
5135 (
5136 movement::previous_subword_start(map, head),
5137 SelectionGoal::None,
5138 )
5139 });
5140 })
5141 }
5142
5143 pub fn delete_to_previous_word_start(
5144 &mut self,
5145 _: &DeleteToPreviousWordStart,
5146 cx: &mut ViewContext<Self>,
5147 ) {
5148 self.transact(cx, |this, cx| {
5149 this.select_autoclose_pair(cx);
5150 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5151 let line_mode = s.line_mode;
5152 s.move_with(|map, selection| {
5153 if selection.is_empty() && !line_mode {
5154 let cursor = movement::previous_word_start(map, selection.head());
5155 selection.set_head(cursor, SelectionGoal::None);
5156 }
5157 });
5158 });
5159 this.insert("", cx);
5160 });
5161 }
5162
5163 pub fn delete_to_previous_subword_start(
5164 &mut self,
5165 _: &DeleteToPreviousSubwordStart,
5166 cx: &mut ViewContext<Self>,
5167 ) {
5168 self.transact(cx, |this, cx| {
5169 this.select_autoclose_pair(cx);
5170 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5171 let line_mode = s.line_mode;
5172 s.move_with(|map, selection| {
5173 if selection.is_empty() && !line_mode {
5174 let cursor = movement::previous_subword_start(map, selection.head());
5175 selection.set_head(cursor, SelectionGoal::None);
5176 }
5177 });
5178 });
5179 this.insert("", cx);
5180 });
5181 }
5182
5183 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
5184 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5185 s.move_cursors_with(|map, head, _| {
5186 (movement::next_word_end(map, head), SelectionGoal::None)
5187 });
5188 })
5189 }
5190
5191 pub fn move_to_next_subword_end(
5192 &mut self,
5193 _: &MoveToNextSubwordEnd,
5194 cx: &mut ViewContext<Self>,
5195 ) {
5196 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5197 s.move_cursors_with(|map, head, _| {
5198 (movement::next_subword_end(map, head), SelectionGoal::None)
5199 });
5200 })
5201 }
5202
5203 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
5204 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5205 s.move_heads_with(|map, head, _| {
5206 (movement::next_word_end(map, head), SelectionGoal::None)
5207 });
5208 })
5209 }
5210
5211 pub fn select_to_next_subword_end(
5212 &mut self,
5213 _: &SelectToNextSubwordEnd,
5214 cx: &mut ViewContext<Self>,
5215 ) {
5216 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5217 s.move_heads_with(|map, head, _| {
5218 (movement::next_subword_end(map, head), SelectionGoal::None)
5219 });
5220 })
5221 }
5222
5223 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
5224 self.transact(cx, |this, cx| {
5225 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5226 let line_mode = s.line_mode;
5227 s.move_with(|map, selection| {
5228 if selection.is_empty() && !line_mode {
5229 let cursor = movement::next_word_end(map, selection.head());
5230 selection.set_head(cursor, SelectionGoal::None);
5231 }
5232 });
5233 });
5234 this.insert("", cx);
5235 });
5236 }
5237
5238 pub fn delete_to_next_subword_end(
5239 &mut self,
5240 _: &DeleteToNextSubwordEnd,
5241 cx: &mut ViewContext<Self>,
5242 ) {
5243 self.transact(cx, |this, cx| {
5244 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5245 s.move_with(|map, selection| {
5246 if selection.is_empty() {
5247 let cursor = movement::next_subword_end(map, selection.head());
5248 selection.set_head(cursor, SelectionGoal::None);
5249 }
5250 });
5251 });
5252 this.insert("", cx);
5253 });
5254 }
5255
5256 pub fn move_to_beginning_of_line(
5257 &mut self,
5258 _: &MoveToBeginningOfLine,
5259 cx: &mut ViewContext<Self>,
5260 ) {
5261 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5262 s.move_cursors_with(|map, head, _| {
5263 (
5264 movement::indented_line_beginning(map, head, true),
5265 SelectionGoal::None,
5266 )
5267 });
5268 })
5269 }
5270
5271 pub fn select_to_beginning_of_line(
5272 &mut self,
5273 action: &SelectToBeginningOfLine,
5274 cx: &mut ViewContext<Self>,
5275 ) {
5276 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5277 s.move_heads_with(|map, head, _| {
5278 (
5279 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
5280 SelectionGoal::None,
5281 )
5282 });
5283 });
5284 }
5285
5286 pub fn delete_to_beginning_of_line(
5287 &mut self,
5288 _: &DeleteToBeginningOfLine,
5289 cx: &mut ViewContext<Self>,
5290 ) {
5291 self.transact(cx, |this, cx| {
5292 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5293 s.move_with(|_, selection| {
5294 selection.reversed = true;
5295 });
5296 });
5297
5298 this.select_to_beginning_of_line(
5299 &SelectToBeginningOfLine {
5300 stop_at_soft_wraps: false,
5301 },
5302 cx,
5303 );
5304 this.backspace(&Backspace, cx);
5305 });
5306 }
5307
5308 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
5309 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5310 s.move_cursors_with(|map, head, _| {
5311 (movement::line_end(map, head, true), SelectionGoal::None)
5312 });
5313 })
5314 }
5315
5316 pub fn select_to_end_of_line(
5317 &mut self,
5318 action: &SelectToEndOfLine,
5319 cx: &mut ViewContext<Self>,
5320 ) {
5321 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5322 s.move_heads_with(|map, head, _| {
5323 (
5324 movement::line_end(map, head, action.stop_at_soft_wraps),
5325 SelectionGoal::None,
5326 )
5327 });
5328 })
5329 }
5330
5331 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
5332 self.transact(cx, |this, cx| {
5333 this.select_to_end_of_line(
5334 &SelectToEndOfLine {
5335 stop_at_soft_wraps: false,
5336 },
5337 cx,
5338 );
5339 this.delete(&Delete, cx);
5340 });
5341 }
5342
5343 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
5344 self.transact(cx, |this, cx| {
5345 this.select_to_end_of_line(
5346 &SelectToEndOfLine {
5347 stop_at_soft_wraps: false,
5348 },
5349 cx,
5350 );
5351 this.cut(&Cut, cx);
5352 });
5353 }
5354
5355 pub fn move_to_start_of_paragraph(
5356 &mut self,
5357 _: &MoveToStartOfParagraph,
5358 cx: &mut ViewContext<Self>,
5359 ) {
5360 if matches!(self.mode, EditorMode::SingleLine) {
5361 cx.propagate_action();
5362 return;
5363 }
5364
5365 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5366 s.move_with(|map, selection| {
5367 selection.collapse_to(
5368 movement::start_of_paragraph(map, selection.head(), 1),
5369 SelectionGoal::None,
5370 )
5371 });
5372 })
5373 }
5374
5375 pub fn move_to_end_of_paragraph(
5376 &mut self,
5377 _: &MoveToEndOfParagraph,
5378 cx: &mut ViewContext<Self>,
5379 ) {
5380 if matches!(self.mode, EditorMode::SingleLine) {
5381 cx.propagate_action();
5382 return;
5383 }
5384
5385 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5386 s.move_with(|map, selection| {
5387 selection.collapse_to(
5388 movement::end_of_paragraph(map, selection.head(), 1),
5389 SelectionGoal::None,
5390 )
5391 });
5392 })
5393 }
5394
5395 pub fn select_to_start_of_paragraph(
5396 &mut self,
5397 _: &SelectToStartOfParagraph,
5398 cx: &mut ViewContext<Self>,
5399 ) {
5400 if matches!(self.mode, EditorMode::SingleLine) {
5401 cx.propagate_action();
5402 return;
5403 }
5404
5405 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5406 s.move_heads_with(|map, head, _| {
5407 (
5408 movement::start_of_paragraph(map, head, 1),
5409 SelectionGoal::None,
5410 )
5411 });
5412 })
5413 }
5414
5415 pub fn select_to_end_of_paragraph(
5416 &mut self,
5417 _: &SelectToEndOfParagraph,
5418 cx: &mut ViewContext<Self>,
5419 ) {
5420 if matches!(self.mode, EditorMode::SingleLine) {
5421 cx.propagate_action();
5422 return;
5423 }
5424
5425 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5426 s.move_heads_with(|map, head, _| {
5427 (
5428 movement::end_of_paragraph(map, head, 1),
5429 SelectionGoal::None,
5430 )
5431 });
5432 })
5433 }
5434
5435 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5436 if matches!(self.mode, EditorMode::SingleLine) {
5437 cx.propagate_action();
5438 return;
5439 }
5440
5441 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5442 s.select_ranges(vec![0..0]);
5443 });
5444 }
5445
5446 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5447 let mut selection = self.selections.last::<Point>(cx);
5448 selection.set_head(Point::zero(), SelectionGoal::None);
5449
5450 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5451 s.select(vec![selection]);
5452 });
5453 }
5454
5455 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5456 if matches!(self.mode, EditorMode::SingleLine) {
5457 cx.propagate_action();
5458 return;
5459 }
5460
5461 let cursor = self.buffer.read(cx).read(cx).len();
5462 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5463 s.select_ranges(vec![cursor..cursor])
5464 });
5465 }
5466
5467 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5468 self.nav_history = nav_history;
5469 }
5470
5471 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5472 self.nav_history.as_ref()
5473 }
5474
5475 fn push_to_nav_history(
5476 &mut self,
5477 cursor_anchor: Anchor,
5478 new_position: Option<Point>,
5479 cx: &mut ViewContext<Self>,
5480 ) {
5481 if let Some(nav_history) = self.nav_history.as_mut() {
5482 let buffer = self.buffer.read(cx).read(cx);
5483 let cursor_position = cursor_anchor.to_point(&buffer);
5484 let scroll_state = self.scroll_manager.anchor();
5485 let scroll_top_row = scroll_state.top_row(&buffer);
5486 drop(buffer);
5487
5488 if let Some(new_position) = new_position {
5489 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5490 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5491 return;
5492 }
5493 }
5494
5495 nav_history.push(
5496 Some(NavigationData {
5497 cursor_anchor,
5498 cursor_position,
5499 scroll_anchor: scroll_state,
5500 scroll_top_row,
5501 }),
5502 cx,
5503 );
5504 }
5505 }
5506
5507 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5508 let buffer = self.buffer.read(cx).snapshot(cx);
5509 let mut selection = self.selections.first::<usize>(cx);
5510 selection.set_head(buffer.len(), SelectionGoal::None);
5511 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5512 s.select(vec![selection]);
5513 });
5514 }
5515
5516 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5517 let end = self.buffer.read(cx).read(cx).len();
5518 self.change_selections(None, cx, |s| {
5519 s.select_ranges(vec![0..end]);
5520 });
5521 }
5522
5523 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5524 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5525 let mut selections = self.selections.all::<Point>(cx);
5526 let max_point = display_map.buffer_snapshot.max_point();
5527 for selection in &mut selections {
5528 let rows = selection.spanned_rows(true, &display_map);
5529 selection.start = Point::new(rows.start, 0);
5530 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5531 selection.reversed = false;
5532 }
5533 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5534 s.select(selections);
5535 });
5536 }
5537
5538 pub fn split_selection_into_lines(
5539 &mut self,
5540 _: &SplitSelectionIntoLines,
5541 cx: &mut ViewContext<Self>,
5542 ) {
5543 let mut to_unfold = Vec::new();
5544 let mut new_selection_ranges = Vec::new();
5545 {
5546 let selections = self.selections.all::<Point>(cx);
5547 let buffer = self.buffer.read(cx).read(cx);
5548 for selection in selections {
5549 for row in selection.start.row..selection.end.row {
5550 let cursor = Point::new(row, buffer.line_len(row));
5551 new_selection_ranges.push(cursor..cursor);
5552 }
5553 new_selection_ranges.push(selection.end..selection.end);
5554 to_unfold.push(selection.start..selection.end);
5555 }
5556 }
5557 self.unfold_ranges(to_unfold, true, true, cx);
5558 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5559 s.select_ranges(new_selection_ranges);
5560 });
5561 }
5562
5563 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5564 self.add_selection(true, cx);
5565 }
5566
5567 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5568 self.add_selection(false, cx);
5569 }
5570
5571 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5572 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5573 let mut selections = self.selections.all::<Point>(cx);
5574 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5575 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5576 let range = oldest_selection.display_range(&display_map).sorted();
5577 let columns = cmp::min(range.start.column(), range.end.column())
5578 ..cmp::max(range.start.column(), range.end.column());
5579
5580 selections.clear();
5581 let mut stack = Vec::new();
5582 for row in range.start.row()..=range.end.row() {
5583 if let Some(selection) = self.selections.build_columnar_selection(
5584 &display_map,
5585 row,
5586 &columns,
5587 oldest_selection.reversed,
5588 ) {
5589 stack.push(selection.id);
5590 selections.push(selection);
5591 }
5592 }
5593
5594 if above {
5595 stack.reverse();
5596 }
5597
5598 AddSelectionsState { above, stack }
5599 });
5600
5601 let last_added_selection = *state.stack.last().unwrap();
5602 let mut new_selections = Vec::new();
5603 if above == state.above {
5604 let end_row = if above {
5605 0
5606 } else {
5607 display_map.max_point().row()
5608 };
5609
5610 'outer: for selection in selections {
5611 if selection.id == last_added_selection {
5612 let range = selection.display_range(&display_map).sorted();
5613 debug_assert_eq!(range.start.row(), range.end.row());
5614 let mut row = range.start.row();
5615 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5616 {
5617 start..end
5618 } else {
5619 cmp::min(range.start.column(), range.end.column())
5620 ..cmp::max(range.start.column(), range.end.column())
5621 };
5622
5623 while row != end_row {
5624 if above {
5625 row -= 1;
5626 } else {
5627 row += 1;
5628 }
5629
5630 if let Some(new_selection) = self.selections.build_columnar_selection(
5631 &display_map,
5632 row,
5633 &columns,
5634 selection.reversed,
5635 ) {
5636 state.stack.push(new_selection.id);
5637 if above {
5638 new_selections.push(new_selection);
5639 new_selections.push(selection);
5640 } else {
5641 new_selections.push(selection);
5642 new_selections.push(new_selection);
5643 }
5644
5645 continue 'outer;
5646 }
5647 }
5648 }
5649
5650 new_selections.push(selection);
5651 }
5652 } else {
5653 new_selections = selections;
5654 new_selections.retain(|s| s.id != last_added_selection);
5655 state.stack.pop();
5656 }
5657
5658 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5659 s.select(new_selections);
5660 });
5661 if state.stack.len() > 1 {
5662 self.add_selections_state = Some(state);
5663 }
5664 }
5665
5666 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5667 self.push_to_selection_history();
5668 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5669 let buffer = &display_map.buffer_snapshot;
5670 let mut selections = self.selections.all::<usize>(cx);
5671 if let Some(mut select_next_state) = self.select_next_state.take() {
5672 let query = &select_next_state.query;
5673 if !select_next_state.done {
5674 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5675 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5676 let mut next_selected_range = None;
5677
5678 let bytes_after_last_selection =
5679 buffer.bytes_in_range(last_selection.end..buffer.len());
5680 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5681 let query_matches = query
5682 .stream_find_iter(bytes_after_last_selection)
5683 .map(|result| (last_selection.end, result))
5684 .chain(
5685 query
5686 .stream_find_iter(bytes_before_first_selection)
5687 .map(|result| (0, result)),
5688 );
5689 for (start_offset, query_match) in query_matches {
5690 let query_match = query_match.unwrap(); // can only fail due to I/O
5691 let offset_range =
5692 start_offset + query_match.start()..start_offset + query_match.end();
5693 let display_range = offset_range.start.to_display_point(&display_map)
5694 ..offset_range.end.to_display_point(&display_map);
5695
5696 if !select_next_state.wordwise
5697 || (!movement::is_inside_word(&display_map, display_range.start)
5698 && !movement::is_inside_word(&display_map, display_range.end))
5699 {
5700 next_selected_range = Some(offset_range);
5701 break;
5702 }
5703 }
5704
5705 if let Some(next_selected_range) = next_selected_range {
5706 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5707 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5708 if action.replace_newest {
5709 s.delete(s.newest_anchor().id);
5710 }
5711 s.insert_range(next_selected_range);
5712 });
5713 } else {
5714 select_next_state.done = true;
5715 }
5716 }
5717
5718 self.select_next_state = Some(select_next_state);
5719 } else if selections.len() == 1 {
5720 let selection = selections.last_mut().unwrap();
5721 if selection.start == selection.end {
5722 let word_range = movement::surrounding_word(
5723 &display_map,
5724 selection.start.to_display_point(&display_map),
5725 );
5726 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5727 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5728 selection.goal = SelectionGoal::None;
5729 selection.reversed = false;
5730
5731 let query = buffer
5732 .text_for_range(selection.start..selection.end)
5733 .collect::<String>();
5734 let select_state = SelectNextState {
5735 query: AhoCorasick::new_auto_configured(&[query]),
5736 wordwise: true,
5737 done: false,
5738 };
5739 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5740 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5741 s.select(selections);
5742 });
5743 self.select_next_state = Some(select_state);
5744 } else {
5745 let query = buffer
5746 .text_for_range(selection.start..selection.end)
5747 .collect::<String>();
5748 self.select_next_state = Some(SelectNextState {
5749 query: AhoCorasick::new_auto_configured(&[query]),
5750 wordwise: false,
5751 done: false,
5752 });
5753 self.select_next(action, cx);
5754 }
5755 }
5756 }
5757
5758 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5759 self.push_to_selection_history();
5760 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5761 let buffer = &display_map.buffer_snapshot;
5762 let mut selections = self.selections.all::<usize>(cx);
5763 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5764 let query = &select_prev_state.query;
5765 if !select_prev_state.done {
5766 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5767 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5768 let mut next_selected_range = None;
5769 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5770 let bytes_before_last_selection =
5771 buffer.reversed_bytes_in_range(0..last_selection.start);
5772 let bytes_after_first_selection =
5773 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5774 let query_matches = query
5775 .stream_find_iter(bytes_before_last_selection)
5776 .map(|result| (last_selection.start, result))
5777 .chain(
5778 query
5779 .stream_find_iter(bytes_after_first_selection)
5780 .map(|result| (buffer.len(), result)),
5781 );
5782 for (end_offset, query_match) in query_matches {
5783 let query_match = query_match.unwrap(); // can only fail due to I/O
5784 let offset_range =
5785 end_offset - query_match.end()..end_offset - query_match.start();
5786 let display_range = offset_range.start.to_display_point(&display_map)
5787 ..offset_range.end.to_display_point(&display_map);
5788
5789 if !select_prev_state.wordwise
5790 || (!movement::is_inside_word(&display_map, display_range.start)
5791 && !movement::is_inside_word(&display_map, display_range.end))
5792 {
5793 next_selected_range = Some(offset_range);
5794 break;
5795 }
5796 }
5797
5798 if let Some(next_selected_range) = next_selected_range {
5799 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5800 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5801 if action.replace_newest {
5802 s.delete(s.newest_anchor().id);
5803 }
5804 s.insert_range(next_selected_range);
5805 });
5806 } else {
5807 select_prev_state.done = true;
5808 }
5809 }
5810
5811 self.select_prev_state = Some(select_prev_state);
5812 } else if selections.len() == 1 {
5813 let selection = selections.last_mut().unwrap();
5814 if selection.start == selection.end {
5815 let word_range = movement::surrounding_word(
5816 &display_map,
5817 selection.start.to_display_point(&display_map),
5818 );
5819 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5820 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5821 selection.goal = SelectionGoal::None;
5822 selection.reversed = false;
5823
5824 let query = buffer
5825 .text_for_range(selection.start..selection.end)
5826 .collect::<String>();
5827 let query = query.chars().rev().collect::<String>();
5828 let select_state = SelectNextState {
5829 query: AhoCorasick::new_auto_configured(&[query]),
5830 wordwise: true,
5831 done: false,
5832 };
5833 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5834 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5835 s.select(selections);
5836 });
5837 self.select_prev_state = Some(select_state);
5838 } else {
5839 let query = buffer
5840 .text_for_range(selection.start..selection.end)
5841 .collect::<String>();
5842 let query = query.chars().rev().collect::<String>();
5843 self.select_prev_state = Some(SelectNextState {
5844 query: AhoCorasick::new_auto_configured(&[query]),
5845 wordwise: false,
5846 done: false,
5847 });
5848 self.select_previous(action, cx);
5849 }
5850 }
5851 }
5852
5853 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5854 self.transact(cx, |this, cx| {
5855 let mut selections = this.selections.all::<Point>(cx);
5856 let mut edits = Vec::new();
5857 let mut selection_edit_ranges = Vec::new();
5858 let mut last_toggled_row = None;
5859 let snapshot = this.buffer.read(cx).read(cx);
5860 let empty_str: Arc<str> = "".into();
5861 let mut suffixes_inserted = Vec::new();
5862
5863 fn comment_prefix_range(
5864 snapshot: &MultiBufferSnapshot,
5865 row: u32,
5866 comment_prefix: &str,
5867 comment_prefix_whitespace: &str,
5868 ) -> Range<Point> {
5869 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5870
5871 let mut line_bytes = snapshot
5872 .bytes_in_range(start..snapshot.max_point())
5873 .flatten()
5874 .copied();
5875
5876 // If this line currently begins with the line comment prefix, then record
5877 // the range containing the prefix.
5878 if line_bytes
5879 .by_ref()
5880 .take(comment_prefix.len())
5881 .eq(comment_prefix.bytes())
5882 {
5883 // Include any whitespace that matches the comment prefix.
5884 let matching_whitespace_len = line_bytes
5885 .zip(comment_prefix_whitespace.bytes())
5886 .take_while(|(a, b)| a == b)
5887 .count() as u32;
5888 let end = Point::new(
5889 start.row,
5890 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5891 );
5892 start..end
5893 } else {
5894 start..start
5895 }
5896 }
5897
5898 fn comment_suffix_range(
5899 snapshot: &MultiBufferSnapshot,
5900 row: u32,
5901 comment_suffix: &str,
5902 comment_suffix_has_leading_space: bool,
5903 ) -> Range<Point> {
5904 let end = Point::new(row, snapshot.line_len(row));
5905 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5906
5907 let mut line_end_bytes = snapshot
5908 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5909 .flatten()
5910 .copied();
5911
5912 let leading_space_len = if suffix_start_column > 0
5913 && line_end_bytes.next() == Some(b' ')
5914 && comment_suffix_has_leading_space
5915 {
5916 1
5917 } else {
5918 0
5919 };
5920
5921 // If this line currently begins with the line comment prefix, then record
5922 // the range containing the prefix.
5923 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5924 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5925 start..end
5926 } else {
5927 end..end
5928 }
5929 }
5930
5931 // TODO: Handle selections that cross excerpts
5932 for selection in &mut selections {
5933 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5934 let language = if let Some(language) =
5935 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5936 {
5937 language
5938 } else {
5939 continue;
5940 };
5941
5942 selection_edit_ranges.clear();
5943
5944 // If multiple selections contain a given row, avoid processing that
5945 // row more than once.
5946 let mut start_row = selection.start.row;
5947 if last_toggled_row == Some(start_row) {
5948 start_row += 1;
5949 }
5950 let end_row =
5951 if selection.end.row > selection.start.row && selection.end.column == 0 {
5952 selection.end.row - 1
5953 } else {
5954 selection.end.row
5955 };
5956 last_toggled_row = Some(end_row);
5957
5958 if start_row > end_row {
5959 continue;
5960 }
5961
5962 // If the language has line comments, toggle those.
5963 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5964 // Split the comment prefix's trailing whitespace into a separate string,
5965 // as that portion won't be used for detecting if a line is a comment.
5966 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5967 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5968 let mut all_selection_lines_are_comments = true;
5969
5970 for row in start_row..=end_row {
5971 if snapshot.is_line_blank(row) && start_row < end_row {
5972 continue;
5973 }
5974
5975 let prefix_range = comment_prefix_range(
5976 snapshot.deref(),
5977 row,
5978 comment_prefix,
5979 comment_prefix_whitespace,
5980 );
5981 if prefix_range.is_empty() {
5982 all_selection_lines_are_comments = false;
5983 }
5984 selection_edit_ranges.push(prefix_range);
5985 }
5986
5987 if all_selection_lines_are_comments {
5988 edits.extend(
5989 selection_edit_ranges
5990 .iter()
5991 .cloned()
5992 .map(|range| (range, empty_str.clone())),
5993 );
5994 } else {
5995 let min_column = selection_edit_ranges
5996 .iter()
5997 .map(|r| r.start.column)
5998 .min()
5999 .unwrap_or(0);
6000 edits.extend(selection_edit_ranges.iter().map(|range| {
6001 let position = Point::new(range.start.row, min_column);
6002 (position..position, full_comment_prefix.clone())
6003 }));
6004 }
6005 } else if let Some((full_comment_prefix, comment_suffix)) =
6006 language.block_comment_delimiters()
6007 {
6008 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6009 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6010 let prefix_range = comment_prefix_range(
6011 snapshot.deref(),
6012 start_row,
6013 comment_prefix,
6014 comment_prefix_whitespace,
6015 );
6016 let suffix_range = comment_suffix_range(
6017 snapshot.deref(),
6018 end_row,
6019 comment_suffix.trim_start_matches(' '),
6020 comment_suffix.starts_with(' '),
6021 );
6022
6023 if prefix_range.is_empty() || suffix_range.is_empty() {
6024 edits.push((
6025 prefix_range.start..prefix_range.start,
6026 full_comment_prefix.clone(),
6027 ));
6028 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6029 suffixes_inserted.push((end_row, comment_suffix.len()));
6030 } else {
6031 edits.push((prefix_range, empty_str.clone()));
6032 edits.push((suffix_range, empty_str.clone()));
6033 }
6034 } else {
6035 continue;
6036 }
6037 }
6038
6039 drop(snapshot);
6040 this.buffer.update(cx, |buffer, cx| {
6041 buffer.edit(edits, None, cx);
6042 });
6043
6044 // Adjust selections so that they end before any comment suffixes that
6045 // were inserted.
6046 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6047 let mut selections = this.selections.all::<Point>(cx);
6048 let snapshot = this.buffer.read(cx).read(cx);
6049 for selection in &mut selections {
6050 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6051 match row.cmp(&selection.end.row) {
6052 Ordering::Less => {
6053 suffixes_inserted.next();
6054 continue;
6055 }
6056 Ordering::Greater => break,
6057 Ordering::Equal => {
6058 if selection.end.column == snapshot.line_len(row) {
6059 if selection.is_empty() {
6060 selection.start.column -= suffix_len as u32;
6061 }
6062 selection.end.column -= suffix_len as u32;
6063 }
6064 break;
6065 }
6066 }
6067 }
6068 }
6069
6070 drop(snapshot);
6071 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6072
6073 let selections = this.selections.all::<Point>(cx);
6074 let selections_on_single_row = selections.windows(2).all(|selections| {
6075 selections[0].start.row == selections[1].start.row
6076 && selections[0].end.row == selections[1].end.row
6077 && selections[0].start.row == selections[0].end.row
6078 });
6079 let selections_selecting = selections
6080 .iter()
6081 .any(|selection| selection.start != selection.end);
6082 let advance_downwards = action.advance_downwards
6083 && selections_on_single_row
6084 && !selections_selecting
6085 && this.mode != EditorMode::SingleLine;
6086
6087 if advance_downwards {
6088 let snapshot = this.buffer.read(cx).snapshot(cx);
6089
6090 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6091 s.move_cursors_with(|display_snapshot, display_point, _| {
6092 let mut point = display_point.to_point(display_snapshot);
6093 point.row += 1;
6094 point = snapshot.clip_point(point, Bias::Left);
6095 let display_point = point.to_display_point(display_snapshot);
6096 (display_point, SelectionGoal::Column(display_point.column()))
6097 })
6098 });
6099 }
6100 });
6101 }
6102
6103 pub fn select_larger_syntax_node(
6104 &mut self,
6105 _: &SelectLargerSyntaxNode,
6106 cx: &mut ViewContext<Self>,
6107 ) {
6108 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6109 let buffer = self.buffer.read(cx).snapshot(cx);
6110 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6111
6112 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6113 let mut selected_larger_node = false;
6114 let new_selections = old_selections
6115 .iter()
6116 .map(|selection| {
6117 let old_range = selection.start..selection.end;
6118 let mut new_range = old_range.clone();
6119 while let Some(containing_range) =
6120 buffer.range_for_syntax_ancestor(new_range.clone())
6121 {
6122 new_range = containing_range;
6123 if !display_map.intersects_fold(new_range.start)
6124 && !display_map.intersects_fold(new_range.end)
6125 {
6126 break;
6127 }
6128 }
6129
6130 selected_larger_node |= new_range != old_range;
6131 Selection {
6132 id: selection.id,
6133 start: new_range.start,
6134 end: new_range.end,
6135 goal: SelectionGoal::None,
6136 reversed: selection.reversed,
6137 }
6138 })
6139 .collect::<Vec<_>>();
6140
6141 if selected_larger_node {
6142 stack.push(old_selections);
6143 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6144 s.select(new_selections);
6145 });
6146 }
6147 self.select_larger_syntax_node_stack = stack;
6148 }
6149
6150 pub fn select_smaller_syntax_node(
6151 &mut self,
6152 _: &SelectSmallerSyntaxNode,
6153 cx: &mut ViewContext<Self>,
6154 ) {
6155 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6156 if let Some(selections) = stack.pop() {
6157 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6158 s.select(selections.to_vec());
6159 });
6160 }
6161 self.select_larger_syntax_node_stack = stack;
6162 }
6163
6164 pub fn move_to_enclosing_bracket(
6165 &mut self,
6166 _: &MoveToEnclosingBracket,
6167 cx: &mut ViewContext<Self>,
6168 ) {
6169 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6170 s.move_offsets_with(|snapshot, selection| {
6171 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
6172 return;
6173 };
6174
6175 let mut best_length = usize::MAX;
6176 let mut best_inside = false;
6177 let mut best_in_bracket_range = false;
6178 let mut best_destination = None;
6179 for (open, close) in enclosing_bracket_ranges {
6180 let close = close.to_inclusive();
6181 let length = close.end() - open.start;
6182 let inside = selection.start >= open.end && selection.end <= *close.start();
6183 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
6184
6185 // If best is next to a bracket and current isn't, skip
6186 if !in_bracket_range && best_in_bracket_range {
6187 continue;
6188 }
6189
6190 // Prefer smaller lengths unless best is inside and current isn't
6191 if length > best_length && (best_inside || !inside) {
6192 continue;
6193 }
6194
6195 best_length = length;
6196 best_inside = inside;
6197 best_in_bracket_range = in_bracket_range;
6198 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
6199 if inside {
6200 open.end
6201 } else {
6202 open.start
6203 }
6204 } else {
6205 if inside {
6206 *close.start()
6207 } else {
6208 *close.end()
6209 }
6210 });
6211 }
6212
6213 if let Some(destination) = best_destination {
6214 selection.collapse_to(destination, SelectionGoal::None);
6215 }
6216 })
6217 });
6218 }
6219
6220 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6221 self.end_selection(cx);
6222 self.selection_history.mode = SelectionHistoryMode::Undoing;
6223 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6224 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6225 self.select_next_state = entry.select_next_state;
6226 self.select_prev_state = entry.select_prev_state;
6227 self.add_selections_state = entry.add_selections_state;
6228 self.request_autoscroll(Autoscroll::newest(), cx);
6229 }
6230 self.selection_history.mode = SelectionHistoryMode::Normal;
6231 }
6232
6233 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6234 self.end_selection(cx);
6235 self.selection_history.mode = SelectionHistoryMode::Redoing;
6236 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6237 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6238 self.select_next_state = entry.select_next_state;
6239 self.select_prev_state = entry.select_prev_state;
6240 self.add_selections_state = entry.add_selections_state;
6241 self.request_autoscroll(Autoscroll::newest(), cx);
6242 }
6243 self.selection_history.mode = SelectionHistoryMode::Normal;
6244 }
6245
6246 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6247 self.go_to_diagnostic_impl(Direction::Next, cx)
6248 }
6249
6250 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6251 self.go_to_diagnostic_impl(Direction::Prev, cx)
6252 }
6253
6254 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6255 let buffer = self.buffer.read(cx).snapshot(cx);
6256 let selection = self.selections.newest::<usize>(cx);
6257
6258 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6259 if direction == Direction::Next {
6260 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6261 let (group_id, jump_to) = popover.activation_info();
6262 if self.activate_diagnostics(group_id, cx) {
6263 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6264 let mut new_selection = s.newest_anchor().clone();
6265 new_selection.collapse_to(jump_to, SelectionGoal::None);
6266 s.select_anchors(vec![new_selection.clone()]);
6267 });
6268 }
6269 return;
6270 }
6271 }
6272
6273 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6274 active_diagnostics
6275 .primary_range
6276 .to_offset(&buffer)
6277 .to_inclusive()
6278 });
6279 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6280 if active_primary_range.contains(&selection.head()) {
6281 *active_primary_range.end()
6282 } else {
6283 selection.head()
6284 }
6285 } else {
6286 selection.head()
6287 };
6288
6289 loop {
6290 let mut diagnostics = if direction == Direction::Prev {
6291 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6292 } else {
6293 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6294 };
6295 let group = diagnostics.find_map(|entry| {
6296 if entry.diagnostic.is_primary
6297 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6298 && !entry.range.is_empty()
6299 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6300 {
6301 Some((entry.range, entry.diagnostic.group_id))
6302 } else {
6303 None
6304 }
6305 });
6306
6307 if let Some((primary_range, group_id)) = group {
6308 if self.activate_diagnostics(group_id, cx) {
6309 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6310 s.select(vec![Selection {
6311 id: selection.id,
6312 start: primary_range.start,
6313 end: primary_range.start,
6314 reversed: false,
6315 goal: SelectionGoal::None,
6316 }]);
6317 });
6318 }
6319 break;
6320 } else {
6321 // Cycle around to the start of the buffer, potentially moving back to the start of
6322 // the currently active diagnostic.
6323 active_primary_range.take();
6324 if direction == Direction::Prev {
6325 if search_start == buffer.len() {
6326 break;
6327 } else {
6328 search_start = buffer.len();
6329 }
6330 } else if search_start == 0 {
6331 break;
6332 } else {
6333 search_start = 0;
6334 }
6335 }
6336 }
6337 }
6338
6339 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6340 let snapshot = self
6341 .display_map
6342 .update(cx, |display_map, cx| display_map.snapshot(cx));
6343 let selection = self.selections.newest::<Point>(cx);
6344
6345 if !self.seek_in_direction(
6346 &snapshot,
6347 selection.head(),
6348 false,
6349 snapshot
6350 .buffer_snapshot
6351 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6352 cx,
6353 ) {
6354 let wrapped_point = Point::zero();
6355 self.seek_in_direction(
6356 &snapshot,
6357 wrapped_point,
6358 true,
6359 snapshot
6360 .buffer_snapshot
6361 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6362 cx,
6363 );
6364 }
6365 }
6366
6367 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6368 let snapshot = self
6369 .display_map
6370 .update(cx, |display_map, cx| display_map.snapshot(cx));
6371 let selection = self.selections.newest::<Point>(cx);
6372
6373 if !self.seek_in_direction(
6374 &snapshot,
6375 selection.head(),
6376 false,
6377 snapshot
6378 .buffer_snapshot
6379 .git_diff_hunks_in_range_rev(0..selection.head().row),
6380 cx,
6381 ) {
6382 let wrapped_point = snapshot.buffer_snapshot.max_point();
6383 self.seek_in_direction(
6384 &snapshot,
6385 wrapped_point,
6386 true,
6387 snapshot
6388 .buffer_snapshot
6389 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6390 cx,
6391 );
6392 }
6393 }
6394
6395 fn seek_in_direction(
6396 &mut self,
6397 snapshot: &DisplaySnapshot,
6398 initial_point: Point,
6399 is_wrapped: bool,
6400 hunks: impl Iterator<Item = DiffHunk<u32>>,
6401 cx: &mut ViewContext<Editor>,
6402 ) -> bool {
6403 let display_point = initial_point.to_display_point(snapshot);
6404 let mut hunks = hunks
6405 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6406 .skip_while(|hunk| {
6407 if is_wrapped {
6408 false
6409 } else {
6410 hunk.contains_display_row(display_point.row())
6411 }
6412 })
6413 .dedup();
6414
6415 if let Some(hunk) = hunks.next() {
6416 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6417 let row = hunk.start_display_row();
6418 let point = DisplayPoint::new(row, 0);
6419 s.select_display_ranges([point..point]);
6420 });
6421
6422 true
6423 } else {
6424 false
6425 }
6426 }
6427
6428 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6429 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6430 }
6431
6432 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6433 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6434 }
6435
6436 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6437 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6438 }
6439
6440 pub fn go_to_type_definition_split(
6441 &mut self,
6442 _: &GoToTypeDefinitionSplit,
6443 cx: &mut ViewContext<Self>,
6444 ) {
6445 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6446 }
6447
6448 fn go_to_definition_of_kind(
6449 &mut self,
6450 kind: GotoDefinitionKind,
6451 split: bool,
6452 cx: &mut ViewContext<Self>,
6453 ) {
6454 let Some(workspace) = self.workspace(cx) else { return };
6455 let buffer = self.buffer.read(cx);
6456 let head = self.selections.newest::<usize>(cx).head();
6457 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6458 text_anchor
6459 } else {
6460 return;
6461 };
6462
6463 let project = workspace.read(cx).project().clone();
6464 let definitions = project.update(cx, |project, cx| match kind {
6465 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6466 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6467 });
6468
6469 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6470 let definitions = definitions.await?;
6471 editor.update(&mut cx, |editor, cx| {
6472 editor.navigate_to_definitions(definitions, split, cx);
6473 })?;
6474 Ok::<(), anyhow::Error>(())
6475 })
6476 .detach_and_log_err(cx);
6477 }
6478
6479 pub fn navigate_to_definitions(
6480 &mut self,
6481 mut definitions: Vec<LocationLink>,
6482 split: bool,
6483 cx: &mut ViewContext<Editor>,
6484 ) {
6485 let Some(workspace) = self.workspace(cx) else { return };
6486 let pane = workspace.read(cx).active_pane().clone();
6487 // If there is one definition, just open it directly
6488 if definitions.len() == 1 {
6489 let definition = definitions.pop().unwrap();
6490 let range = definition
6491 .target
6492 .range
6493 .to_offset(definition.target.buffer.read(cx));
6494
6495 let range = self.range_for_match(&range);
6496 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6497 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6498 s.select_ranges([range]);
6499 });
6500 } else {
6501 cx.window_context().defer(move |cx| {
6502 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6503 if split {
6504 workspace.split_project_item(definition.target.buffer.clone(), cx)
6505 } else {
6506 workspace.open_project_item(definition.target.buffer.clone(), cx)
6507 }
6508 });
6509 target_editor.update(cx, |target_editor, cx| {
6510 // When selecting a definition in a different buffer, disable the nav history
6511 // to avoid creating a history entry at the previous cursor location.
6512 pane.update(cx, |pane, _| pane.disable_history());
6513 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6514 s.select_ranges([range]);
6515 });
6516 pane.update(cx, |pane, _| pane.enable_history());
6517 });
6518 });
6519 }
6520 } else if !definitions.is_empty() {
6521 let replica_id = self.replica_id(cx);
6522 cx.window_context().defer(move |cx| {
6523 let title = definitions
6524 .iter()
6525 .find(|definition| definition.origin.is_some())
6526 .and_then(|definition| {
6527 definition.origin.as_ref().map(|origin| {
6528 let buffer = origin.buffer.read(cx);
6529 format!(
6530 "Definitions for {}",
6531 buffer
6532 .text_for_range(origin.range.clone())
6533 .collect::<String>()
6534 )
6535 })
6536 })
6537 .unwrap_or("Definitions".to_owned());
6538 let locations = definitions
6539 .into_iter()
6540 .map(|definition| definition.target)
6541 .collect();
6542 workspace.update(cx, |workspace, cx| {
6543 Self::open_locations_in_multibuffer(
6544 workspace, locations, replica_id, title, split, cx,
6545 )
6546 });
6547 });
6548 }
6549 }
6550
6551 pub fn find_all_references(
6552 workspace: &mut Workspace,
6553 _: &FindAllReferences,
6554 cx: &mut ViewContext<Workspace>,
6555 ) -> Option<Task<Result<()>>> {
6556 let active_item = workspace.active_item(cx)?;
6557 let editor_handle = active_item.act_as::<Self>(cx)?;
6558
6559 let editor = editor_handle.read(cx);
6560 let buffer = editor.buffer.read(cx);
6561 let head = editor.selections.newest::<usize>(cx).head();
6562 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6563 let replica_id = editor.replica_id(cx);
6564
6565 let project = workspace.project().clone();
6566 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6567 Some(cx.spawn_labeled(
6568 "Finding All References...",
6569 |workspace, mut cx| async move {
6570 let locations = references.await?;
6571 if locations.is_empty() {
6572 return Ok(());
6573 }
6574
6575 workspace.update(&mut cx, |workspace, cx| {
6576 let title = locations
6577 .first()
6578 .as_ref()
6579 .map(|location| {
6580 let buffer = location.buffer.read(cx);
6581 format!(
6582 "References to `{}`",
6583 buffer
6584 .text_for_range(location.range.clone())
6585 .collect::<String>()
6586 )
6587 })
6588 .unwrap();
6589 Self::open_locations_in_multibuffer(
6590 workspace, locations, replica_id, title, false, cx,
6591 );
6592 })?;
6593
6594 Ok(())
6595 },
6596 ))
6597 }
6598
6599 /// Opens a multibuffer with the given project locations in it
6600 pub fn open_locations_in_multibuffer(
6601 workspace: &mut Workspace,
6602 mut locations: Vec<Location>,
6603 replica_id: ReplicaId,
6604 title: String,
6605 split: bool,
6606 cx: &mut ViewContext<Workspace>,
6607 ) {
6608 // If there are multiple definitions, open them in a multibuffer
6609 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6610 let mut locations = locations.into_iter().peekable();
6611 let mut ranges_to_highlight = Vec::new();
6612
6613 let excerpt_buffer = cx.add_model(|cx| {
6614 let mut multibuffer = MultiBuffer::new(replica_id);
6615 while let Some(location) = locations.next() {
6616 let buffer = location.buffer.read(cx);
6617 let mut ranges_for_buffer = Vec::new();
6618 let range = location.range.to_offset(buffer);
6619 ranges_for_buffer.push(range.clone());
6620
6621 while let Some(next_location) = locations.peek() {
6622 if next_location.buffer == location.buffer {
6623 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6624 locations.next();
6625 } else {
6626 break;
6627 }
6628 }
6629
6630 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6631 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6632 location.buffer.clone(),
6633 ranges_for_buffer,
6634 1,
6635 cx,
6636 ))
6637 }
6638
6639 multibuffer.with_title(title)
6640 });
6641
6642 let editor = cx.add_view(|cx| {
6643 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6644 });
6645 editor.update(cx, |editor, cx| {
6646 editor.highlight_background::<Self>(
6647 ranges_to_highlight,
6648 |theme| theme.editor.highlighted_line_background,
6649 cx,
6650 );
6651 });
6652 if split {
6653 workspace.split_item(Box::new(editor), cx);
6654 } else {
6655 workspace.add_item(Box::new(editor), cx);
6656 }
6657 }
6658
6659 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6660 use language::ToOffset as _;
6661
6662 let project = self.project.clone()?;
6663 let selection = self.selections.newest_anchor().clone();
6664 let (cursor_buffer, cursor_buffer_position) = self
6665 .buffer
6666 .read(cx)
6667 .text_anchor_for_position(selection.head(), cx)?;
6668 let (tail_buffer, _) = self
6669 .buffer
6670 .read(cx)
6671 .text_anchor_for_position(selection.tail(), cx)?;
6672 if tail_buffer != cursor_buffer {
6673 return None;
6674 }
6675
6676 let snapshot = cursor_buffer.read(cx).snapshot();
6677 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6678 let prepare_rename = project.update(cx, |project, cx| {
6679 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6680 });
6681
6682 Some(cx.spawn(|this, mut cx| async move {
6683 let rename_range = if let Some(range) = prepare_rename.await? {
6684 Some(range)
6685 } else {
6686 this.read_with(&cx, |this, cx| {
6687 let buffer = this.buffer.read(cx).snapshot(cx);
6688 let mut buffer_highlights = this
6689 .document_highlights_for_position(selection.head(), &buffer)
6690 .filter(|highlight| {
6691 highlight.start.excerpt_id() == selection.head().excerpt_id()
6692 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6693 });
6694 buffer_highlights
6695 .next()
6696 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6697 })?
6698 };
6699 if let Some(rename_range) = rename_range {
6700 let rename_buffer_range = rename_range.to_offset(&snapshot);
6701 let cursor_offset_in_rename_range =
6702 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6703
6704 this.update(&mut cx, |this, cx| {
6705 this.take_rename(false, cx);
6706 let style = this.style(cx);
6707 let buffer = this.buffer.read(cx).read(cx);
6708 let cursor_offset = selection.head().to_offset(&buffer);
6709 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6710 let rename_end = rename_start + rename_buffer_range.len();
6711 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6712 let mut old_highlight_id = None;
6713 let old_name: Arc<str> = buffer
6714 .chunks(rename_start..rename_end, true)
6715 .map(|chunk| {
6716 if old_highlight_id.is_none() {
6717 old_highlight_id = chunk.syntax_highlight_id;
6718 }
6719 chunk.text
6720 })
6721 .collect::<String>()
6722 .into();
6723
6724 drop(buffer);
6725
6726 // Position the selection in the rename editor so that it matches the current selection.
6727 this.show_local_selections = false;
6728 let rename_editor = cx.add_view(|cx| {
6729 let mut editor = Editor::single_line(None, cx);
6730 if let Some(old_highlight_id) = old_highlight_id {
6731 editor.override_text_style =
6732 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6733 }
6734 editor.buffer.update(cx, |buffer, cx| {
6735 buffer.edit([(0..0, old_name.clone())], None, cx)
6736 });
6737 editor.select_all(&SelectAll, cx);
6738 editor
6739 });
6740
6741 let ranges = this
6742 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6743 .into_iter()
6744 .flat_map(|(_, ranges)| ranges)
6745 .chain(
6746 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6747 .into_iter()
6748 .flat_map(|(_, ranges)| ranges),
6749 )
6750 .collect();
6751
6752 this.highlight_text::<Rename>(
6753 ranges,
6754 HighlightStyle {
6755 fade_out: Some(style.rename_fade),
6756 ..Default::default()
6757 },
6758 cx,
6759 );
6760 cx.focus(&rename_editor);
6761 let block_id = this.insert_blocks(
6762 [BlockProperties {
6763 style: BlockStyle::Flex,
6764 position: range.start.clone(),
6765 height: 1,
6766 render: Arc::new({
6767 let editor = rename_editor.clone();
6768 move |cx: &mut BlockContext| {
6769 ChildView::new(&editor, cx)
6770 .contained()
6771 .with_padding_left(cx.anchor_x)
6772 .into_any()
6773 }
6774 }),
6775 disposition: BlockDisposition::Below,
6776 }],
6777 Some(Autoscroll::fit()),
6778 cx,
6779 )[0];
6780 this.pending_rename = Some(RenameState {
6781 range,
6782 old_name,
6783 editor: rename_editor,
6784 block_id,
6785 });
6786 })?;
6787 }
6788
6789 Ok(())
6790 }))
6791 }
6792
6793 pub fn confirm_rename(
6794 workspace: &mut Workspace,
6795 _: &ConfirmRename,
6796 cx: &mut ViewContext<Workspace>,
6797 ) -> Option<Task<Result<()>>> {
6798 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6799
6800 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6801 let rename = editor.take_rename(false, cx)?;
6802 let buffer = editor.buffer.read(cx);
6803 let (start_buffer, start) =
6804 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6805 let (end_buffer, end) =
6806 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6807 if start_buffer == end_buffer {
6808 let new_name = rename.editor.read(cx).text(cx);
6809 Some((start_buffer, start..end, rename.old_name, new_name))
6810 } else {
6811 None
6812 }
6813 })?;
6814
6815 let rename = workspace.project().clone().update(cx, |project, cx| {
6816 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6817 });
6818
6819 let editor = editor.downgrade();
6820 Some(cx.spawn(|workspace, mut cx| async move {
6821 let project_transaction = rename.await?;
6822 Self::open_project_transaction(
6823 &editor,
6824 workspace,
6825 project_transaction,
6826 format!("Rename: {} → {}", old_name, new_name),
6827 cx.clone(),
6828 )
6829 .await?;
6830
6831 editor.update(&mut cx, |editor, cx| {
6832 editor.refresh_document_highlights(cx);
6833 })?;
6834 Ok(())
6835 }))
6836 }
6837
6838 fn take_rename(
6839 &mut self,
6840 moving_cursor: bool,
6841 cx: &mut ViewContext<Self>,
6842 ) -> Option<RenameState> {
6843 let rename = self.pending_rename.take()?;
6844 self.remove_blocks(
6845 [rename.block_id].into_iter().collect(),
6846 Some(Autoscroll::fit()),
6847 cx,
6848 );
6849 self.clear_text_highlights::<Rename>(cx);
6850 self.show_local_selections = true;
6851
6852 if moving_cursor {
6853 let rename_editor = rename.editor.read(cx);
6854 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6855
6856 // Update the selection to match the position of the selection inside
6857 // the rename editor.
6858 let snapshot = self.buffer.read(cx).read(cx);
6859 let rename_range = rename.range.to_offset(&snapshot);
6860 let cursor_in_editor = snapshot
6861 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6862 .min(rename_range.end);
6863 drop(snapshot);
6864
6865 self.change_selections(None, cx, |s| {
6866 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6867 });
6868 } else {
6869 self.refresh_document_highlights(cx);
6870 }
6871
6872 Some(rename)
6873 }
6874
6875 #[cfg(any(test, feature = "test-support"))]
6876 pub fn pending_rename(&self) -> Option<&RenameState> {
6877 self.pending_rename.as_ref()
6878 }
6879
6880 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6881 let project = match &self.project {
6882 Some(project) => project.clone(),
6883 None => return None,
6884 };
6885
6886 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6887 }
6888
6889 fn perform_format(
6890 &mut self,
6891 project: ModelHandle<Project>,
6892 trigger: FormatTrigger,
6893 cx: &mut ViewContext<Self>,
6894 ) -> Task<Result<()>> {
6895 let buffer = self.buffer().clone();
6896 let buffers = buffer.read(cx).all_buffers();
6897
6898 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6899 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6900
6901 cx.spawn(|_, mut cx| async move {
6902 let transaction = futures::select_biased! {
6903 _ = timeout => {
6904 log::warn!("timed out waiting for formatting");
6905 None
6906 }
6907 transaction = format.log_err().fuse() => transaction,
6908 };
6909
6910 buffer.update(&mut cx, |buffer, cx| {
6911 if let Some(transaction) = transaction {
6912 if !buffer.is_singleton() {
6913 buffer.push_transaction(&transaction.0, cx);
6914 }
6915 }
6916
6917 cx.notify();
6918 });
6919
6920 Ok(())
6921 })
6922 }
6923
6924 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6925 if let Some(project) = self.project.clone() {
6926 self.buffer.update(cx, |multi_buffer, cx| {
6927 project.update(cx, |project, cx| {
6928 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6929 });
6930 })
6931 }
6932 }
6933
6934 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6935 cx.show_character_palette();
6936 }
6937
6938 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6939 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6940 let buffer = self.buffer.read(cx).snapshot(cx);
6941 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6942 let is_valid = buffer
6943 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6944 .any(|entry| {
6945 entry.diagnostic.is_primary
6946 && !entry.range.is_empty()
6947 && entry.range.start == primary_range_start
6948 && entry.diagnostic.message == active_diagnostics.primary_message
6949 });
6950
6951 if is_valid != active_diagnostics.is_valid {
6952 active_diagnostics.is_valid = is_valid;
6953 let mut new_styles = HashMap::default();
6954 for (block_id, diagnostic) in &active_diagnostics.blocks {
6955 new_styles.insert(
6956 *block_id,
6957 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6958 );
6959 }
6960 self.display_map
6961 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6962 }
6963 }
6964 }
6965
6966 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6967 self.dismiss_diagnostics(cx);
6968 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6969 let buffer = self.buffer.read(cx).snapshot(cx);
6970
6971 let mut primary_range = None;
6972 let mut primary_message = None;
6973 let mut group_end = Point::zero();
6974 let diagnostic_group = buffer
6975 .diagnostic_group::<Point>(group_id)
6976 .map(|entry| {
6977 if entry.range.end > group_end {
6978 group_end = entry.range.end;
6979 }
6980 if entry.diagnostic.is_primary {
6981 primary_range = Some(entry.range.clone());
6982 primary_message = Some(entry.diagnostic.message.clone());
6983 }
6984 entry
6985 })
6986 .collect::<Vec<_>>();
6987 let primary_range = primary_range?;
6988 let primary_message = primary_message?;
6989 let primary_range =
6990 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6991
6992 let blocks = display_map
6993 .insert_blocks(
6994 diagnostic_group.iter().map(|entry| {
6995 let diagnostic = entry.diagnostic.clone();
6996 let message_height = diagnostic.message.lines().count() as u8;
6997 BlockProperties {
6998 style: BlockStyle::Fixed,
6999 position: buffer.anchor_after(entry.range.start),
7000 height: message_height,
7001 render: diagnostic_block_renderer(diagnostic, true),
7002 disposition: BlockDisposition::Below,
7003 }
7004 }),
7005 cx,
7006 )
7007 .into_iter()
7008 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7009 .collect();
7010
7011 Some(ActiveDiagnosticGroup {
7012 primary_range,
7013 primary_message,
7014 blocks,
7015 is_valid: true,
7016 })
7017 });
7018 self.active_diagnostics.is_some()
7019 }
7020
7021 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7022 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7023 self.display_map.update(cx, |display_map, cx| {
7024 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7025 });
7026 cx.notify();
7027 }
7028 }
7029
7030 pub fn set_selections_from_remote(
7031 &mut self,
7032 selections: Vec<Selection<Anchor>>,
7033 pending_selection: Option<Selection<Anchor>>,
7034 cx: &mut ViewContext<Self>,
7035 ) {
7036 let old_cursor_position = self.selections.newest_anchor().head();
7037 self.selections.change_with(cx, |s| {
7038 s.select_anchors(selections);
7039 if let Some(pending_selection) = pending_selection {
7040 s.set_pending(pending_selection, SelectMode::Character);
7041 } else {
7042 s.clear_pending();
7043 }
7044 });
7045 self.selections_did_change(false, &old_cursor_position, cx);
7046 }
7047
7048 fn push_to_selection_history(&mut self) {
7049 self.selection_history.push(SelectionHistoryEntry {
7050 selections: self.selections.disjoint_anchors(),
7051 select_next_state: self.select_next_state.clone(),
7052 select_prev_state: self.select_prev_state.clone(),
7053 add_selections_state: self.add_selections_state.clone(),
7054 });
7055 }
7056
7057 pub fn transact(
7058 &mut self,
7059 cx: &mut ViewContext<Self>,
7060 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7061 ) -> Option<TransactionId> {
7062 self.start_transaction_at(Instant::now(), cx);
7063 update(self, cx);
7064 self.end_transaction_at(Instant::now(), cx)
7065 }
7066
7067 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7068 self.end_selection(cx);
7069 if let Some(tx_id) = self
7070 .buffer
7071 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7072 {
7073 self.selection_history
7074 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7075 }
7076 }
7077
7078 fn end_transaction_at(
7079 &mut self,
7080 now: Instant,
7081 cx: &mut ViewContext<Self>,
7082 ) -> Option<TransactionId> {
7083 if let Some(tx_id) = self
7084 .buffer
7085 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7086 {
7087 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7088 *end_selections = Some(self.selections.disjoint_anchors());
7089 } else {
7090 error!("unexpectedly ended a transaction that wasn't started by this editor");
7091 }
7092
7093 cx.emit(Event::Edited);
7094 Some(tx_id)
7095 } else {
7096 None
7097 }
7098 }
7099
7100 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7101 let mut fold_ranges = Vec::new();
7102
7103 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7104
7105 let selections = self.selections.all::<Point>(cx);
7106 for selection in selections {
7107 let range = selection.range().sorted();
7108 let buffer_start_row = range.start.row;
7109
7110 for row in (0..=range.end.row).rev() {
7111 let fold_range = display_map.foldable_range(row);
7112
7113 if let Some(fold_range) = fold_range {
7114 if fold_range.end.row >= buffer_start_row {
7115 fold_ranges.push(fold_range);
7116 if row <= range.start.row {
7117 break;
7118 }
7119 }
7120 }
7121 }
7122 }
7123
7124 self.fold_ranges(fold_ranges, true, cx);
7125 }
7126
7127 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7128 let buffer_row = fold_at.buffer_row;
7129 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7130
7131 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7132 let autoscroll = self
7133 .selections
7134 .all::<Point>(cx)
7135 .iter()
7136 .any(|selection| fold_range.overlaps(&selection.range()));
7137
7138 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7139 }
7140 }
7141
7142 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7143 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7144 let buffer = &display_map.buffer_snapshot;
7145 let selections = self.selections.all::<Point>(cx);
7146 let ranges = selections
7147 .iter()
7148 .map(|s| {
7149 let range = s.display_range(&display_map).sorted();
7150 let mut start = range.start.to_point(&display_map);
7151 let mut end = range.end.to_point(&display_map);
7152 start.column = 0;
7153 end.column = buffer.line_len(end.row);
7154 start..end
7155 })
7156 .collect::<Vec<_>>();
7157
7158 self.unfold_ranges(ranges, true, true, cx);
7159 }
7160
7161 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7162 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7163
7164 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7165 ..Point::new(
7166 unfold_at.buffer_row,
7167 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7168 );
7169
7170 let autoscroll = self
7171 .selections
7172 .all::<Point>(cx)
7173 .iter()
7174 .any(|selection| selection.range().overlaps(&intersection_range));
7175
7176 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7177 }
7178
7179 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7180 let selections = self.selections.all::<Point>(cx);
7181 let ranges = selections.into_iter().map(|s| s.start..s.end);
7182 self.fold_ranges(ranges, true, cx);
7183 }
7184
7185 pub fn fold_ranges<T: ToOffset + Clone>(
7186 &mut self,
7187 ranges: impl IntoIterator<Item = Range<T>>,
7188 auto_scroll: bool,
7189 cx: &mut ViewContext<Self>,
7190 ) {
7191 let mut ranges = ranges.into_iter().peekable();
7192 if ranges.peek().is_some() {
7193 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7194
7195 if auto_scroll {
7196 self.request_autoscroll(Autoscroll::fit(), cx);
7197 }
7198
7199 cx.notify();
7200 }
7201 }
7202
7203 pub fn unfold_ranges<T: ToOffset + Clone>(
7204 &mut self,
7205 ranges: impl IntoIterator<Item = Range<T>>,
7206 inclusive: bool,
7207 auto_scroll: bool,
7208 cx: &mut ViewContext<Self>,
7209 ) {
7210 let mut ranges = ranges.into_iter().peekable();
7211 if ranges.peek().is_some() {
7212 self.display_map
7213 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7214 if auto_scroll {
7215 self.request_autoscroll(Autoscroll::fit(), cx);
7216 }
7217
7218 cx.notify();
7219 }
7220 }
7221
7222 pub fn gutter_hover(
7223 &mut self,
7224 GutterHover { hovered }: &GutterHover,
7225 cx: &mut ViewContext<Self>,
7226 ) {
7227 self.gutter_hovered = *hovered;
7228 cx.notify();
7229 }
7230
7231 pub fn insert_blocks(
7232 &mut self,
7233 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7234 autoscroll: Option<Autoscroll>,
7235 cx: &mut ViewContext<Self>,
7236 ) -> Vec<BlockId> {
7237 let blocks = self
7238 .display_map
7239 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7240 if let Some(autoscroll) = autoscroll {
7241 self.request_autoscroll(autoscroll, cx);
7242 }
7243 blocks
7244 }
7245
7246 pub fn replace_blocks(
7247 &mut self,
7248 blocks: HashMap<BlockId, RenderBlock>,
7249 autoscroll: Option<Autoscroll>,
7250 cx: &mut ViewContext<Self>,
7251 ) {
7252 self.display_map
7253 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7254 if let Some(autoscroll) = autoscroll {
7255 self.request_autoscroll(autoscroll, cx);
7256 }
7257 }
7258
7259 pub fn remove_blocks(
7260 &mut self,
7261 block_ids: HashSet<BlockId>,
7262 autoscroll: Option<Autoscroll>,
7263 cx: &mut ViewContext<Self>,
7264 ) {
7265 self.display_map.update(cx, |display_map, cx| {
7266 display_map.remove_blocks(block_ids, cx)
7267 });
7268 if let Some(autoscroll) = autoscroll {
7269 self.request_autoscroll(autoscroll, cx);
7270 }
7271 }
7272
7273 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7274 self.display_map
7275 .update(cx, |map, cx| map.snapshot(cx))
7276 .longest_row()
7277 }
7278
7279 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7280 self.display_map
7281 .update(cx, |map, cx| map.snapshot(cx))
7282 .max_point()
7283 }
7284
7285 pub fn text(&self, cx: &AppContext) -> String {
7286 self.buffer.read(cx).read(cx).text()
7287 }
7288
7289 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7290 self.transact(cx, |this, cx| {
7291 this.buffer
7292 .read(cx)
7293 .as_singleton()
7294 .expect("you can only call set_text on editors for singleton buffers")
7295 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7296 });
7297 }
7298
7299 pub fn display_text(&self, cx: &mut AppContext) -> String {
7300 self.display_map
7301 .update(cx, |map, cx| map.snapshot(cx))
7302 .text()
7303 }
7304
7305 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7306 let mut wrap_guides = smallvec::smallvec![];
7307
7308 if self.show_wrap_guides == Some(false) {
7309 return wrap_guides;
7310 }
7311
7312 let settings = self.buffer.read(cx).settings_at(0, cx);
7313 if settings.show_wrap_guides {
7314 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7315 wrap_guides.push((soft_wrap as usize, true));
7316 }
7317 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7318 }
7319
7320 wrap_guides
7321 }
7322
7323 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7324 let settings = self.buffer.read(cx).settings_at(0, cx);
7325 let mode = self
7326 .soft_wrap_mode_override
7327 .unwrap_or_else(|| settings.soft_wrap);
7328 match mode {
7329 language_settings::SoftWrap::None => SoftWrap::None,
7330 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7331 language_settings::SoftWrap::PreferredLineLength => {
7332 SoftWrap::Column(settings.preferred_line_length)
7333 }
7334 }
7335 }
7336
7337 pub fn set_soft_wrap_mode(
7338 &mut self,
7339 mode: language_settings::SoftWrap,
7340 cx: &mut ViewContext<Self>,
7341 ) {
7342 self.soft_wrap_mode_override = Some(mode);
7343 cx.notify();
7344 }
7345
7346 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7347 self.display_map
7348 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7349 }
7350
7351 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7352 if self.soft_wrap_mode_override.is_some() {
7353 self.soft_wrap_mode_override.take();
7354 } else {
7355 let soft_wrap = match self.soft_wrap_mode(cx) {
7356 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7357 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7358 };
7359 self.soft_wrap_mode_override = Some(soft_wrap);
7360 }
7361 cx.notify();
7362 }
7363
7364 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7365 self.show_gutter = show_gutter;
7366 cx.notify();
7367 }
7368
7369 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7370 self.show_wrap_guides = Some(show_gutter);
7371 cx.notify();
7372 }
7373
7374 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7375 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7376 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7377 cx.reveal_path(&file.abs_path(cx));
7378 }
7379 }
7380 }
7381
7382 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7383 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7384 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7385 if let Some(path) = file.abs_path(cx).to_str() {
7386 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7387 }
7388 }
7389 }
7390 }
7391
7392 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7393 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7394 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7395 if let Some(path) = file.path().to_str() {
7396 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7397 }
7398 }
7399 }
7400 }
7401
7402 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7403 self.highlighted_rows = rows;
7404 }
7405
7406 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7407 self.highlighted_rows.clone()
7408 }
7409
7410 pub fn highlight_background<T: 'static>(
7411 &mut self,
7412 ranges: Vec<Range<Anchor>>,
7413 color_fetcher: fn(&Theme) -> Color,
7414 cx: &mut ViewContext<Self>,
7415 ) {
7416 self.background_highlights
7417 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7418 cx.notify();
7419 }
7420
7421 #[allow(clippy::type_complexity)]
7422 pub fn clear_background_highlights<T: 'static>(
7423 &mut self,
7424 cx: &mut ViewContext<Self>,
7425 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7426 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7427 if highlights.is_some() {
7428 cx.notify();
7429 }
7430 highlights
7431 }
7432
7433 #[cfg(feature = "test-support")]
7434 pub fn all_background_highlights(
7435 &mut self,
7436 cx: &mut ViewContext<Self>,
7437 ) -> Vec<(Range<DisplayPoint>, Color)> {
7438 let snapshot = self.snapshot(cx);
7439 let buffer = &snapshot.buffer_snapshot;
7440 let start = buffer.anchor_before(0);
7441 let end = buffer.anchor_after(buffer.len());
7442 let theme = theme::current(cx);
7443 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7444 }
7445
7446 fn document_highlights_for_position<'a>(
7447 &'a self,
7448 position: Anchor,
7449 buffer: &'a MultiBufferSnapshot,
7450 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7451 let read_highlights = self
7452 .background_highlights
7453 .get(&TypeId::of::<DocumentHighlightRead>())
7454 .map(|h| &h.1);
7455 let write_highlights = self
7456 .background_highlights
7457 .get(&TypeId::of::<DocumentHighlightWrite>())
7458 .map(|h| &h.1);
7459 let left_position = position.bias_left(buffer);
7460 let right_position = position.bias_right(buffer);
7461 read_highlights
7462 .into_iter()
7463 .chain(write_highlights)
7464 .flat_map(move |ranges| {
7465 let start_ix = match ranges.binary_search_by(|probe| {
7466 let cmp = probe.end.cmp(&left_position, buffer);
7467 if cmp.is_ge() {
7468 Ordering::Greater
7469 } else {
7470 Ordering::Less
7471 }
7472 }) {
7473 Ok(i) | Err(i) => i,
7474 };
7475
7476 let right_position = right_position.clone();
7477 ranges[start_ix..]
7478 .iter()
7479 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7480 })
7481 }
7482
7483 pub fn background_highlights_in_range(
7484 &self,
7485 search_range: Range<Anchor>,
7486 display_snapshot: &DisplaySnapshot,
7487 theme: &Theme,
7488 ) -> Vec<(Range<DisplayPoint>, Color)> {
7489 let mut results = Vec::new();
7490 let buffer = &display_snapshot.buffer_snapshot;
7491 for (color_fetcher, ranges) in self.background_highlights.values() {
7492 let color = color_fetcher(theme);
7493 let start_ix = match ranges.binary_search_by(|probe| {
7494 let cmp = probe.end.cmp(&search_range.start, buffer);
7495 if cmp.is_gt() {
7496 Ordering::Greater
7497 } else {
7498 Ordering::Less
7499 }
7500 }) {
7501 Ok(i) | Err(i) => i,
7502 };
7503 for range in &ranges[start_ix..] {
7504 if range.start.cmp(&search_range.end, buffer).is_ge() {
7505 break;
7506 }
7507 let start = range
7508 .start
7509 .to_point(buffer)
7510 .to_display_point(display_snapshot);
7511 let end = range
7512 .end
7513 .to_point(buffer)
7514 .to_display_point(display_snapshot);
7515 results.push((start..end, color))
7516 }
7517 }
7518 results
7519 }
7520 pub fn background_highlights_in_range_for<T: 'static>(
7521 &self,
7522 search_range: Range<Anchor>,
7523 display_snapshot: &DisplaySnapshot,
7524 theme: &Theme,
7525 ) -> Vec<(Range<DisplayPoint>, Color)> {
7526 let mut results = Vec::new();
7527 let buffer = &display_snapshot.buffer_snapshot;
7528 let Some((color_fetcher, ranges)) = self.background_highlights
7529 .get(&TypeId::of::<T>()) else {
7530 return vec![];
7531 };
7532
7533 let color = color_fetcher(theme);
7534 let start_ix = match ranges.binary_search_by(|probe| {
7535 let cmp = probe.end.cmp(&search_range.start, buffer);
7536 if cmp.is_gt() {
7537 Ordering::Greater
7538 } else {
7539 Ordering::Less
7540 }
7541 }) {
7542 Ok(i) | Err(i) => i,
7543 };
7544 for range in &ranges[start_ix..] {
7545 if range.start.cmp(&search_range.end, buffer).is_ge() {
7546 break;
7547 }
7548 let start = range
7549 .start
7550 .to_point(buffer)
7551 .to_display_point(display_snapshot);
7552 let end = range
7553 .end
7554 .to_point(buffer)
7555 .to_display_point(display_snapshot);
7556 results.push((start..end, color))
7557 }
7558
7559 results
7560 }
7561
7562 pub fn background_highlight_row_ranges<T: 'static>(
7563 &self,
7564 search_range: Range<Anchor>,
7565 display_snapshot: &DisplaySnapshot,
7566 count: usize,
7567 ) -> Vec<RangeInclusive<DisplayPoint>> {
7568 let mut results = Vec::new();
7569 let buffer = &display_snapshot.buffer_snapshot;
7570 let Some((_, ranges)) = self.background_highlights
7571 .get(&TypeId::of::<T>()) else {
7572 return vec![];
7573 };
7574
7575 let start_ix = match ranges.binary_search_by(|probe| {
7576 let cmp = probe.end.cmp(&search_range.start, buffer);
7577 if cmp.is_gt() {
7578 Ordering::Greater
7579 } else {
7580 Ordering::Less
7581 }
7582 }) {
7583 Ok(i) | Err(i) => i,
7584 };
7585 let mut push_region = |start: Option<Point>, end: Option<Point>| {
7586 if let (Some(start_display), Some(end_display)) = (start, end) {
7587 results.push(
7588 start_display.to_display_point(display_snapshot)
7589 ..=end_display.to_display_point(display_snapshot),
7590 );
7591 }
7592 };
7593 let mut start_row: Option<Point> = None;
7594 let mut end_row: Option<Point> = None;
7595 if ranges.len() > count {
7596 return vec![];
7597 }
7598 for range in &ranges[start_ix..] {
7599 if range.start.cmp(&search_range.end, buffer).is_ge() {
7600 break;
7601 }
7602 let end = range.end.to_point(buffer);
7603 if let Some(current_row) = &end_row {
7604 if end.row == current_row.row {
7605 continue;
7606 }
7607 }
7608 let start = range.start.to_point(buffer);
7609
7610 if start_row.is_none() {
7611 assert_eq!(end_row, None);
7612 start_row = Some(start);
7613 end_row = Some(end);
7614 continue;
7615 }
7616 if let Some(current_end) = end_row.as_mut() {
7617 if start.row > current_end.row + 1 {
7618 push_region(start_row, end_row);
7619 start_row = Some(start);
7620 end_row = Some(end);
7621 } else {
7622 // Merge two hunks.
7623 *current_end = end;
7624 }
7625 } else {
7626 unreachable!();
7627 }
7628 }
7629 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
7630 push_region(start_row, end_row);
7631 results
7632 }
7633
7634 pub fn highlight_text<T: 'static>(
7635 &mut self,
7636 ranges: Vec<Range<Anchor>>,
7637 style: HighlightStyle,
7638 cx: &mut ViewContext<Self>,
7639 ) {
7640 self.display_map.update(cx, |map, _| {
7641 map.highlight_text(TypeId::of::<T>(), ranges, style)
7642 });
7643 cx.notify();
7644 }
7645
7646 pub fn text_highlights<'a, T: 'static>(
7647 &'a self,
7648 cx: &'a AppContext,
7649 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7650 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7651 }
7652
7653 pub fn clear_text_highlights<T: 'static>(
7654 &mut self,
7655 cx: &mut ViewContext<Self>,
7656 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7657 let highlights = self
7658 .display_map
7659 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7660 if highlights.is_some() {
7661 cx.notify();
7662 }
7663 highlights
7664 }
7665
7666 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7667 self.blink_manager.read(cx).visible() && self.focused
7668 }
7669
7670 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7671 cx.notify();
7672 }
7673
7674 fn on_buffer_event(
7675 &mut self,
7676 multibuffer: ModelHandle<MultiBuffer>,
7677 event: &multi_buffer::Event,
7678 cx: &mut ViewContext<Self>,
7679 ) {
7680 match event {
7681 multi_buffer::Event::Edited => {
7682 self.refresh_active_diagnostics(cx);
7683 self.refresh_code_actions(cx);
7684 if self.has_active_copilot_suggestion(cx) {
7685 self.update_visible_copilot_suggestion(cx);
7686 }
7687 cx.emit(Event::BufferEdited);
7688
7689 if let Some(project) = &self.project {
7690 let project = project.read(cx);
7691 let languages_affected = multibuffer
7692 .read(cx)
7693 .all_buffers()
7694 .into_iter()
7695 .filter_map(|buffer| {
7696 let buffer = buffer.read(cx);
7697 let language = buffer.language()?;
7698 if project.is_local()
7699 && project.language_servers_for_buffer(buffer, cx).count() == 0
7700 {
7701 None
7702 } else {
7703 Some(language)
7704 }
7705 })
7706 .cloned()
7707 .collect::<HashSet<_>>();
7708 if !languages_affected.is_empty() {
7709 self.refresh_inlays(
7710 InlayRefreshReason::BufferEdited(languages_affected),
7711 cx,
7712 );
7713 }
7714 }
7715 }
7716 multi_buffer::Event::ExcerptsAdded {
7717 buffer,
7718 predecessor,
7719 excerpts,
7720 } => cx.emit(Event::ExcerptsAdded {
7721 buffer: buffer.clone(),
7722 predecessor: *predecessor,
7723 excerpts: excerpts.clone(),
7724 }),
7725 multi_buffer::Event::ExcerptsRemoved { ids } => {
7726 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7727 }
7728 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7729 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7730 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7731 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7732 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7733 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7734 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7735 multi_buffer::Event::DiagnosticsUpdated => {
7736 self.refresh_active_diagnostics(cx);
7737 }
7738 _ => {}
7739 };
7740 }
7741
7742 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7743 cx.notify();
7744 }
7745
7746 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7747 self.refresh_copilot_suggestions(true, cx);
7748 self.refresh_inlays(
7749 InlayRefreshReason::SettingsChange(inlay_hint_settings(
7750 self.selections.newest_anchor().head(),
7751 &self.buffer.read(cx).snapshot(cx),
7752 cx,
7753 )),
7754 cx,
7755 );
7756 }
7757
7758 pub fn set_searchable(&mut self, searchable: bool) {
7759 self.searchable = searchable;
7760 }
7761
7762 pub fn searchable(&self) -> bool {
7763 self.searchable
7764 }
7765
7766 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7767 let active_item = workspace.active_item(cx);
7768 let editor_handle = if let Some(editor) = active_item
7769 .as_ref()
7770 .and_then(|item| item.act_as::<Self>(cx))
7771 {
7772 editor
7773 } else {
7774 cx.propagate_action();
7775 return;
7776 };
7777
7778 let editor = editor_handle.read(cx);
7779 let buffer = editor.buffer.read(cx);
7780 if buffer.is_singleton() {
7781 cx.propagate_action();
7782 return;
7783 }
7784
7785 let mut new_selections_by_buffer = HashMap::default();
7786 for selection in editor.selections.all::<usize>(cx) {
7787 for (buffer, mut range, _) in
7788 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7789 {
7790 if selection.reversed {
7791 mem::swap(&mut range.start, &mut range.end);
7792 }
7793 new_selections_by_buffer
7794 .entry(buffer)
7795 .or_insert(Vec::new())
7796 .push(range)
7797 }
7798 }
7799
7800 editor_handle.update(cx, |editor, cx| {
7801 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7802 });
7803 let pane = workspace.active_pane().clone();
7804 pane.update(cx, |pane, _| pane.disable_history());
7805
7806 // We defer the pane interaction because we ourselves are a workspace item
7807 // and activating a new item causes the pane to call a method on us reentrantly,
7808 // which panics if we're on the stack.
7809 cx.defer(move |workspace, cx| {
7810 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7811 let editor = workspace.open_project_item::<Self>(buffer, cx);
7812 editor.update(cx, |editor, cx| {
7813 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7814 s.select_ranges(ranges);
7815 });
7816 });
7817 }
7818
7819 pane.update(cx, |pane, _| pane.enable_history());
7820 });
7821 }
7822
7823 fn jump(
7824 workspace: &mut Workspace,
7825 path: ProjectPath,
7826 position: Point,
7827 anchor: language::Anchor,
7828 cx: &mut ViewContext<Workspace>,
7829 ) {
7830 let editor = workspace.open_path(path, None, true, cx);
7831 cx.spawn(|_, mut cx| async move {
7832 let editor = editor
7833 .await?
7834 .downcast::<Editor>()
7835 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7836 .downgrade();
7837 editor.update(&mut cx, |editor, cx| {
7838 let buffer = editor
7839 .buffer()
7840 .read(cx)
7841 .as_singleton()
7842 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7843 let buffer = buffer.read(cx);
7844 let cursor = if buffer.can_resolve(&anchor) {
7845 language::ToPoint::to_point(&anchor, buffer)
7846 } else {
7847 buffer.clip_point(position, Bias::Left)
7848 };
7849
7850 let nav_history = editor.nav_history.take();
7851 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7852 s.select_ranges([cursor..cursor]);
7853 });
7854 editor.nav_history = nav_history;
7855
7856 anyhow::Ok(())
7857 })??;
7858
7859 anyhow::Ok(())
7860 })
7861 .detach_and_log_err(cx);
7862 }
7863
7864 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7865 let snapshot = self.buffer.read(cx).read(cx);
7866 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7867 Some(
7868 ranges
7869 .iter()
7870 .map(move |range| {
7871 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7872 })
7873 .collect(),
7874 )
7875 }
7876
7877 fn selection_replacement_ranges(
7878 &self,
7879 range: Range<OffsetUtf16>,
7880 cx: &AppContext,
7881 ) -> Vec<Range<OffsetUtf16>> {
7882 let selections = self.selections.all::<OffsetUtf16>(cx);
7883 let newest_selection = selections
7884 .iter()
7885 .max_by_key(|selection| selection.id)
7886 .unwrap();
7887 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7888 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7889 let snapshot = self.buffer.read(cx).read(cx);
7890 selections
7891 .into_iter()
7892 .map(|mut selection| {
7893 selection.start.0 =
7894 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7895 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7896 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7897 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7898 })
7899 .collect()
7900 }
7901
7902 fn report_copilot_event(
7903 &self,
7904 suggestion_id: Option<String>,
7905 suggestion_accepted: bool,
7906 cx: &AppContext,
7907 ) {
7908 let Some(project) = &self.project else {
7909 return
7910 };
7911
7912 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7913 let file_extension = self
7914 .buffer
7915 .read(cx)
7916 .as_singleton()
7917 .and_then(|b| b.read(cx).file())
7918 .and_then(|file| Path::new(file.file_name(cx)).extension())
7919 .and_then(|e| e.to_str())
7920 .map(|a| a.to_string());
7921
7922 let telemetry = project.read(cx).client().telemetry().clone();
7923 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7924
7925 let event = ClickhouseEvent::Copilot {
7926 suggestion_id,
7927 suggestion_accepted,
7928 file_extension,
7929 };
7930 telemetry.report_clickhouse_event(event, telemetry_settings);
7931 }
7932
7933 fn report_editor_event(
7934 &self,
7935 operation: &'static str,
7936 file_extension: Option<String>,
7937 cx: &AppContext,
7938 ) {
7939 let Some(project) = &self.project else {
7940 return
7941 };
7942
7943 // If None, we are in a file without an extension
7944 let file = self
7945 .buffer
7946 .read(cx)
7947 .as_singleton()
7948 .and_then(|b| b.read(cx).file());
7949 let file_extension = file_extension.or(file
7950 .as_ref()
7951 .and_then(|file| Path::new(file.file_name(cx)).extension())
7952 .and_then(|e| e.to_str())
7953 .map(|a| a.to_string()));
7954
7955 let vim_mode = cx
7956 .global::<SettingsStore>()
7957 .raw_user_settings()
7958 .get("vim_mode")
7959 == Some(&serde_json::Value::Bool(true));
7960 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7961 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7962 let copilot_enabled_for_language = self
7963 .buffer
7964 .read(cx)
7965 .settings_at(0, cx)
7966 .show_copilot_suggestions;
7967
7968 let telemetry = project.read(cx).client().telemetry().clone();
7969 let event = ClickhouseEvent::Editor {
7970 file_extension,
7971 vim_mode,
7972 operation,
7973 copilot_enabled,
7974 copilot_enabled_for_language,
7975 };
7976 telemetry.report_clickhouse_event(event, telemetry_settings)
7977 }
7978
7979 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7980 /// with each line being an array of {text, highlight} objects.
7981 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7982 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7983 return;
7984 };
7985
7986 #[derive(Serialize)]
7987 struct Chunk<'a> {
7988 text: String,
7989 highlight: Option<&'a str>,
7990 }
7991
7992 let snapshot = buffer.read(cx).snapshot();
7993 let range = self
7994 .selected_text_range(cx)
7995 .and_then(|selected_range| {
7996 if selected_range.is_empty() {
7997 None
7998 } else {
7999 Some(selected_range)
8000 }
8001 })
8002 .unwrap_or_else(|| 0..snapshot.len());
8003
8004 let chunks = snapshot.chunks(range, true);
8005 let mut lines = Vec::new();
8006 let mut line: VecDeque<Chunk> = VecDeque::new();
8007
8008 let theme = &theme::current(cx).editor.syntax;
8009
8010 for chunk in chunks {
8011 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8012 let mut chunk_lines = chunk.text.split("\n").peekable();
8013 while let Some(text) = chunk_lines.next() {
8014 let mut merged_with_last_token = false;
8015 if let Some(last_token) = line.back_mut() {
8016 if last_token.highlight == highlight {
8017 last_token.text.push_str(text);
8018 merged_with_last_token = true;
8019 }
8020 }
8021
8022 if !merged_with_last_token {
8023 line.push_back(Chunk {
8024 text: text.into(),
8025 highlight,
8026 });
8027 }
8028
8029 if chunk_lines.peek().is_some() {
8030 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8031 line.pop_front();
8032 }
8033 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8034 line.pop_back();
8035 }
8036
8037 lines.push(mem::take(&mut line));
8038 }
8039 }
8040 }
8041
8042 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
8043 cx.write_to_clipboard(ClipboardItem::new(lines));
8044 }
8045
8046 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8047 &self.inlay_hint_cache
8048 }
8049}
8050
8051fn inlay_hint_settings(
8052 location: Anchor,
8053 snapshot: &MultiBufferSnapshot,
8054 cx: &mut ViewContext<'_, '_, Editor>,
8055) -> InlayHintSettings {
8056 let file = snapshot.file_at(location);
8057 let language = snapshot.language_at(location);
8058 let settings = all_language_settings(file, cx);
8059 settings
8060 .language(language.map(|l| l.name()).as_deref())
8061 .inlay_hints
8062}
8063
8064fn consume_contiguous_rows(
8065 contiguous_row_selections: &mut Vec<Selection<Point>>,
8066 selection: &Selection<Point>,
8067 display_map: &DisplaySnapshot,
8068 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8069) -> (u32, u32) {
8070 contiguous_row_selections.push(selection.clone());
8071 let start_row = selection.start.row;
8072 let mut end_row = ending_row(selection, display_map);
8073
8074 while let Some(next_selection) = selections.peek() {
8075 if next_selection.start.row <= end_row {
8076 end_row = ending_row(next_selection, display_map);
8077 contiguous_row_selections.push(selections.next().unwrap().clone());
8078 } else {
8079 break;
8080 }
8081 }
8082 (start_row, end_row)
8083}
8084
8085fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8086 if next_selection.end.column > 0 || next_selection.is_empty() {
8087 display_map.next_line_boundary(next_selection.end).0.row + 1
8088 } else {
8089 next_selection.end.row
8090 }
8091}
8092
8093impl EditorSnapshot {
8094 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8095 self.display_snapshot.buffer_snapshot.language_at(position)
8096 }
8097
8098 pub fn is_focused(&self) -> bool {
8099 self.is_focused
8100 }
8101
8102 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8103 self.placeholder_text.as_ref()
8104 }
8105
8106 pub fn scroll_position(&self) -> Vector2F {
8107 self.scroll_anchor.scroll_position(&self.display_snapshot)
8108 }
8109}
8110
8111impl Deref for EditorSnapshot {
8112 type Target = DisplaySnapshot;
8113
8114 fn deref(&self) -> &Self::Target {
8115 &self.display_snapshot
8116 }
8117}
8118
8119#[derive(Clone, Debug, PartialEq, Eq)]
8120pub enum Event {
8121 InputIgnored {
8122 text: Arc<str>,
8123 },
8124 ExcerptsAdded {
8125 buffer: ModelHandle<Buffer>,
8126 predecessor: ExcerptId,
8127 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8128 },
8129 ExcerptsRemoved {
8130 ids: Vec<ExcerptId>,
8131 },
8132 BufferEdited,
8133 Edited,
8134 Reparsed,
8135 Focused,
8136 Blurred,
8137 DirtyChanged,
8138 Saved,
8139 TitleChanged,
8140 DiffBaseChanged,
8141 SelectionsChanged {
8142 local: bool,
8143 },
8144 ScrollPositionChanged {
8145 local: bool,
8146 autoscroll: bool,
8147 },
8148 Closed,
8149}
8150
8151pub struct EditorFocused(pub ViewHandle<Editor>);
8152pub struct EditorBlurred(pub ViewHandle<Editor>);
8153pub struct EditorReleased(pub WeakViewHandle<Editor>);
8154
8155impl Entity for Editor {
8156 type Event = Event;
8157
8158 fn release(&mut self, cx: &mut AppContext) {
8159 cx.emit_global(EditorReleased(self.handle.clone()));
8160 }
8161}
8162
8163impl View for Editor {
8164 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8165 let style = self.style(cx);
8166 let font_changed = self.display_map.update(cx, |map, cx| {
8167 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8168 map.set_font(style.text.font_id, style.text.font_size, cx)
8169 });
8170
8171 if font_changed {
8172 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8173 hide_hover(editor, cx);
8174 hide_link_definition(editor, cx);
8175 });
8176 }
8177
8178 Stack::new()
8179 .with_child(EditorElement::new(style.clone()))
8180 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8181 .into_any()
8182 }
8183
8184 fn ui_name() -> &'static str {
8185 "Editor"
8186 }
8187
8188 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8189 if cx.is_self_focused() {
8190 let focused_event = EditorFocused(cx.handle());
8191 cx.emit(Event::Focused);
8192 cx.emit_global(focused_event);
8193 }
8194 if let Some(rename) = self.pending_rename.as_ref() {
8195 cx.focus(&rename.editor);
8196 } else {
8197 if !self.focused {
8198 self.blink_manager.update(cx, BlinkManager::enable);
8199 }
8200 self.focused = true;
8201 self.buffer.update(cx, |buffer, cx| {
8202 buffer.finalize_last_transaction(cx);
8203 if self.leader_replica_id.is_none() {
8204 buffer.set_active_selections(
8205 &self.selections.disjoint_anchors(),
8206 self.selections.line_mode,
8207 self.cursor_shape,
8208 cx,
8209 );
8210 }
8211 });
8212 }
8213 }
8214
8215 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8216 let blurred_event = EditorBlurred(cx.handle());
8217 cx.emit_global(blurred_event);
8218 self.focused = false;
8219 self.blink_manager.update(cx, BlinkManager::disable);
8220 self.buffer
8221 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8222 self.hide_context_menu(cx);
8223 hide_hover(self, cx);
8224 cx.emit(Event::Blurred);
8225 cx.notify();
8226 }
8227
8228 fn modifiers_changed(
8229 &mut self,
8230 event: &gpui::platform::ModifiersChangedEvent,
8231 cx: &mut ViewContext<Self>,
8232 ) -> bool {
8233 let pending_selection = self.has_pending_selection();
8234
8235 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
8236 if event.cmd && !pending_selection {
8237 let snapshot = self.snapshot(cx);
8238 let kind = if event.shift {
8239 LinkDefinitionKind::Type
8240 } else {
8241 LinkDefinitionKind::Symbol
8242 };
8243
8244 show_link_definition(kind, self, point, snapshot, cx);
8245 return false;
8246 }
8247 }
8248
8249 {
8250 if self.link_go_to_definition_state.symbol_range.is_some()
8251 || !self.link_go_to_definition_state.definitions.is_empty()
8252 {
8253 self.link_go_to_definition_state.symbol_range.take();
8254 self.link_go_to_definition_state.definitions.clear();
8255 cx.notify();
8256 }
8257
8258 self.link_go_to_definition_state.task = None;
8259
8260 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8261 }
8262
8263 false
8264 }
8265
8266 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8267 Self::reset_to_default_keymap_context(keymap);
8268 let mode = match self.mode {
8269 EditorMode::SingleLine => "single_line",
8270 EditorMode::AutoHeight { .. } => "auto_height",
8271 EditorMode::Full => "full",
8272 };
8273 keymap.add_key("mode", mode);
8274 if self.pending_rename.is_some() {
8275 keymap.add_identifier("renaming");
8276 }
8277 match self.context_menu.as_ref() {
8278 Some(ContextMenu::Completions(_)) => {
8279 keymap.add_identifier("menu");
8280 keymap.add_identifier("showing_completions")
8281 }
8282 Some(ContextMenu::CodeActions(_)) => {
8283 keymap.add_identifier("menu");
8284 keymap.add_identifier("showing_code_actions")
8285 }
8286 None => {}
8287 }
8288 for layer in self.keymap_context_layers.values() {
8289 keymap.extend(layer);
8290 }
8291
8292 if let Some(extension) = self
8293 .buffer
8294 .read(cx)
8295 .as_singleton()
8296 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8297 {
8298 keymap.add_key("extension", extension.to_string());
8299 }
8300 }
8301
8302 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8303 Some(
8304 self.buffer
8305 .read(cx)
8306 .read(cx)
8307 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8308 .collect(),
8309 )
8310 }
8311
8312 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8313 // Prevent the IME menu from appearing when holding down an alphabetic key
8314 // while input is disabled.
8315 if !self.input_enabled {
8316 return None;
8317 }
8318
8319 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8320 Some(range.start.0..range.end.0)
8321 }
8322
8323 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8324 let snapshot = self.buffer.read(cx).read(cx);
8325 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8326 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8327 }
8328
8329 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8330 self.clear_text_highlights::<InputComposition>(cx);
8331 self.ime_transaction.take();
8332 }
8333
8334 fn replace_text_in_range(
8335 &mut self,
8336 range_utf16: Option<Range<usize>>,
8337 text: &str,
8338 cx: &mut ViewContext<Self>,
8339 ) {
8340 self.transact(cx, |this, cx| {
8341 if this.input_enabled {
8342 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8343 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8344 Some(this.selection_replacement_ranges(range_utf16, cx))
8345 } else {
8346 this.marked_text_ranges(cx)
8347 };
8348
8349 if let Some(new_selected_ranges) = new_selected_ranges {
8350 this.change_selections(None, cx, |selections| {
8351 selections.select_ranges(new_selected_ranges)
8352 });
8353 }
8354 }
8355
8356 this.handle_input(text, cx);
8357 });
8358
8359 if !self.input_enabled {
8360 return;
8361 }
8362
8363 if let Some(transaction) = self.ime_transaction {
8364 self.buffer.update(cx, |buffer, cx| {
8365 buffer.group_until_transaction(transaction, cx);
8366 });
8367 }
8368
8369 self.unmark_text(cx);
8370 }
8371
8372 fn replace_and_mark_text_in_range(
8373 &mut self,
8374 range_utf16: Option<Range<usize>>,
8375 text: &str,
8376 new_selected_range_utf16: Option<Range<usize>>,
8377 cx: &mut ViewContext<Self>,
8378 ) {
8379 if !self.input_enabled {
8380 return;
8381 }
8382
8383 let transaction = self.transact(cx, |this, cx| {
8384 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8385 let snapshot = this.buffer.read(cx).read(cx);
8386 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8387 for marked_range in &mut marked_ranges {
8388 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8389 marked_range.start.0 += relative_range_utf16.start;
8390 marked_range.start =
8391 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8392 marked_range.end =
8393 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8394 }
8395 }
8396 Some(marked_ranges)
8397 } else if let Some(range_utf16) = range_utf16 {
8398 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8399 Some(this.selection_replacement_ranges(range_utf16, cx))
8400 } else {
8401 None
8402 };
8403
8404 if let Some(ranges) = ranges_to_replace {
8405 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8406 }
8407
8408 let marked_ranges = {
8409 let snapshot = this.buffer.read(cx).read(cx);
8410 this.selections
8411 .disjoint_anchors()
8412 .iter()
8413 .map(|selection| {
8414 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8415 })
8416 .collect::<Vec<_>>()
8417 };
8418
8419 if text.is_empty() {
8420 this.unmark_text(cx);
8421 } else {
8422 this.highlight_text::<InputComposition>(
8423 marked_ranges.clone(),
8424 this.style(cx).composition_mark,
8425 cx,
8426 );
8427 }
8428
8429 this.handle_input(text, cx);
8430
8431 if let Some(new_selected_range) = new_selected_range_utf16 {
8432 let snapshot = this.buffer.read(cx).read(cx);
8433 let new_selected_ranges = marked_ranges
8434 .into_iter()
8435 .map(|marked_range| {
8436 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8437 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8438 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8439 snapshot.clip_offset_utf16(new_start, Bias::Left)
8440 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8441 })
8442 .collect::<Vec<_>>();
8443
8444 drop(snapshot);
8445 this.change_selections(None, cx, |selections| {
8446 selections.select_ranges(new_selected_ranges)
8447 });
8448 }
8449 });
8450
8451 self.ime_transaction = self.ime_transaction.or(transaction);
8452 if let Some(transaction) = self.ime_transaction {
8453 self.buffer.update(cx, |buffer, cx| {
8454 buffer.group_until_transaction(transaction, cx);
8455 });
8456 }
8457
8458 if self.text_highlights::<InputComposition>(cx).is_none() {
8459 self.ime_transaction.take();
8460 }
8461 }
8462}
8463
8464fn build_style(
8465 settings: &ThemeSettings,
8466 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8467 override_text_style: Option<&OverrideTextStyle>,
8468 cx: &AppContext,
8469) -> EditorStyle {
8470 let font_cache = cx.font_cache();
8471 let line_height_scalar = settings.line_height();
8472 let theme_id = settings.theme.meta.id;
8473 let mut theme = settings.theme.editor.clone();
8474 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8475 let field_editor_theme = get_field_editor_theme(&settings.theme);
8476 theme.text_color = field_editor_theme.text.color;
8477 theme.selection = field_editor_theme.selection;
8478 theme.background = field_editor_theme
8479 .container
8480 .background_color
8481 .unwrap_or_default();
8482 EditorStyle {
8483 text: field_editor_theme.text,
8484 placeholder_text: field_editor_theme.placeholder_text,
8485 line_height_scalar,
8486 theme,
8487 theme_id,
8488 }
8489 } else {
8490 let font_family_id = settings.buffer_font_family;
8491 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8492 let font_properties = Default::default();
8493 let font_id = font_cache
8494 .select_font(font_family_id, &font_properties)
8495 .unwrap();
8496 let font_size = settings.buffer_font_size(cx);
8497 EditorStyle {
8498 text: TextStyle {
8499 color: settings.theme.editor.text_color,
8500 font_family_name,
8501 font_family_id,
8502 font_id,
8503 font_size,
8504 font_properties,
8505 underline: Default::default(),
8506 },
8507 placeholder_text: None,
8508 line_height_scalar,
8509 theme,
8510 theme_id,
8511 }
8512 };
8513
8514 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8515 if let Some(highlighted) = style
8516 .text
8517 .clone()
8518 .highlight(highlight_style, font_cache)
8519 .log_err()
8520 {
8521 style.text = highlighted;
8522 }
8523 }
8524
8525 style
8526}
8527
8528trait SelectionExt {
8529 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8530 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8531 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8532 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8533 -> Range<u32>;
8534}
8535
8536impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8537 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8538 let start = self.start.to_point(buffer);
8539 let end = self.end.to_point(buffer);
8540 if self.reversed {
8541 end..start
8542 } else {
8543 start..end
8544 }
8545 }
8546
8547 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8548 let start = self.start.to_offset(buffer);
8549 let end = self.end.to_offset(buffer);
8550 if self.reversed {
8551 end..start
8552 } else {
8553 start..end
8554 }
8555 }
8556
8557 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8558 let start = self
8559 .start
8560 .to_point(&map.buffer_snapshot)
8561 .to_display_point(map);
8562 let end = self
8563 .end
8564 .to_point(&map.buffer_snapshot)
8565 .to_display_point(map);
8566 if self.reversed {
8567 end..start
8568 } else {
8569 start..end
8570 }
8571 }
8572
8573 fn spanned_rows(
8574 &self,
8575 include_end_if_at_line_start: bool,
8576 map: &DisplaySnapshot,
8577 ) -> Range<u32> {
8578 let start = self.start.to_point(&map.buffer_snapshot);
8579 let mut end = self.end.to_point(&map.buffer_snapshot);
8580 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8581 end.row -= 1;
8582 }
8583
8584 let buffer_start = map.prev_line_boundary(start).0;
8585 let buffer_end = map.next_line_boundary(end).0;
8586 buffer_start.row..buffer_end.row + 1
8587 }
8588}
8589
8590impl<T: InvalidationRegion> InvalidationStack<T> {
8591 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8592 where
8593 S: Clone + ToOffset,
8594 {
8595 while let Some(region) = self.last() {
8596 let all_selections_inside_invalidation_ranges =
8597 if selections.len() == region.ranges().len() {
8598 selections
8599 .iter()
8600 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8601 .all(|(selection, invalidation_range)| {
8602 let head = selection.head().to_offset(buffer);
8603 invalidation_range.start <= head && invalidation_range.end >= head
8604 })
8605 } else {
8606 false
8607 };
8608
8609 if all_selections_inside_invalidation_ranges {
8610 break;
8611 } else {
8612 self.pop();
8613 }
8614 }
8615 }
8616}
8617
8618impl<T> Default for InvalidationStack<T> {
8619 fn default() -> Self {
8620 Self(Default::default())
8621 }
8622}
8623
8624impl<T> Deref for InvalidationStack<T> {
8625 type Target = Vec<T>;
8626
8627 fn deref(&self) -> &Self::Target {
8628 &self.0
8629 }
8630}
8631
8632impl<T> DerefMut for InvalidationStack<T> {
8633 fn deref_mut(&mut self) -> &mut Self::Target {
8634 &mut self.0
8635 }
8636}
8637
8638impl InvalidationRegion for SnippetState {
8639 fn ranges(&self) -> &[Range<Anchor>] {
8640 &self.ranges[self.active_index]
8641 }
8642}
8643
8644impl Deref for EditorStyle {
8645 type Target = theme::Editor;
8646
8647 fn deref(&self) -> &Self::Target {
8648 &self.theme
8649 }
8650}
8651
8652pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8653 let mut highlighted_lines = Vec::new();
8654
8655 for (index, line) in diagnostic.message.lines().enumerate() {
8656 let line = match &diagnostic.source {
8657 Some(source) if index == 0 => {
8658 let source_highlight = Vec::from_iter(0..source.len());
8659 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8660 }
8661
8662 _ => highlight_diagnostic_message(Vec::new(), line),
8663 };
8664 highlighted_lines.push(line);
8665 }
8666 let message = diagnostic.message;
8667 Arc::new(move |cx: &mut BlockContext| {
8668 let message = message.clone();
8669 let settings = settings::get::<ThemeSettings>(cx);
8670 let tooltip_style = settings.theme.tooltip.clone();
8671 let theme = &settings.theme.editor;
8672 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8673 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8674 let anchor_x = cx.anchor_x;
8675 enum BlockContextToolip {}
8676 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
8677 Flex::column()
8678 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8679 Label::new(
8680 line.clone(),
8681 style.message.clone().with_font_size(font_size),
8682 )
8683 .with_highlights(highlights.clone())
8684 .contained()
8685 .with_margin_left(anchor_x)
8686 }))
8687 .aligned()
8688 .left()
8689 .into_any()
8690 })
8691 .with_cursor_style(CursorStyle::PointingHand)
8692 .on_click(MouseButton::Left, move |_, _, cx| {
8693 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8694 })
8695 // We really need to rethink this ID system...
8696 .with_tooltip::<BlockContextToolip>(
8697 cx.block_id,
8698 "Copy diagnostic message",
8699 None,
8700 tooltip_style,
8701 cx,
8702 )
8703 .into_any()
8704 })
8705}
8706
8707pub fn highlight_diagnostic_message(
8708 initial_highlights: Vec<usize>,
8709 message: &str,
8710) -> (String, Vec<usize>) {
8711 let mut message_without_backticks = String::new();
8712 let mut prev_offset = 0;
8713 let mut inside_block = false;
8714 let mut highlights = initial_highlights;
8715 for (match_ix, (offset, _)) in message
8716 .match_indices('`')
8717 .chain([(message.len(), "")])
8718 .enumerate()
8719 {
8720 message_without_backticks.push_str(&message[prev_offset..offset]);
8721 if inside_block {
8722 highlights.extend(prev_offset - match_ix..offset - match_ix);
8723 }
8724
8725 inside_block = !inside_block;
8726 prev_offset = offset + 1;
8727 }
8728
8729 (message_without_backticks, highlights)
8730}
8731
8732pub fn diagnostic_style(
8733 severity: DiagnosticSeverity,
8734 valid: bool,
8735 theme: &theme::Editor,
8736) -> DiagnosticStyle {
8737 match (severity, valid) {
8738 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8739 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8740 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8741 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8742 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8743 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8744 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8745 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8746 _ => theme.invalid_hint_diagnostic.clone(),
8747 }
8748}
8749
8750pub fn combine_syntax_and_fuzzy_match_highlights(
8751 text: &str,
8752 default_style: HighlightStyle,
8753 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8754 match_indices: &[usize],
8755) -> Vec<(Range<usize>, HighlightStyle)> {
8756 let mut result = Vec::new();
8757 let mut match_indices = match_indices.iter().copied().peekable();
8758
8759 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8760 {
8761 syntax_highlight.weight = None;
8762
8763 // Add highlights for any fuzzy match characters before the next
8764 // syntax highlight range.
8765 while let Some(&match_index) = match_indices.peek() {
8766 if match_index >= range.start {
8767 break;
8768 }
8769 match_indices.next();
8770 let end_index = char_ix_after(match_index, text);
8771 let mut match_style = default_style;
8772 match_style.weight = Some(fonts::Weight::BOLD);
8773 result.push((match_index..end_index, match_style));
8774 }
8775
8776 if range.start == usize::MAX {
8777 break;
8778 }
8779
8780 // Add highlights for any fuzzy match characters within the
8781 // syntax highlight range.
8782 let mut offset = range.start;
8783 while let Some(&match_index) = match_indices.peek() {
8784 if match_index >= range.end {
8785 break;
8786 }
8787
8788 match_indices.next();
8789 if match_index > offset {
8790 result.push((offset..match_index, syntax_highlight));
8791 }
8792
8793 let mut end_index = char_ix_after(match_index, text);
8794 while let Some(&next_match_index) = match_indices.peek() {
8795 if next_match_index == end_index && next_match_index < range.end {
8796 end_index = char_ix_after(next_match_index, text);
8797 match_indices.next();
8798 } else {
8799 break;
8800 }
8801 }
8802
8803 let mut match_style = syntax_highlight;
8804 match_style.weight = Some(fonts::Weight::BOLD);
8805 result.push((match_index..end_index, match_style));
8806 offset = end_index;
8807 }
8808
8809 if offset < range.end {
8810 result.push((offset..range.end, syntax_highlight));
8811 }
8812 }
8813
8814 fn char_ix_after(ix: usize, text: &str) -> usize {
8815 ix + text[ix..].chars().next().unwrap().len_utf8()
8816 }
8817
8818 result
8819}
8820
8821pub fn styled_runs_for_code_label<'a>(
8822 label: &'a CodeLabel,
8823 syntax_theme: &'a theme::SyntaxTheme,
8824) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8825 let fade_out = HighlightStyle {
8826 fade_out: Some(0.35),
8827 ..Default::default()
8828 };
8829
8830 let mut prev_end = label.filter_range.end;
8831 label
8832 .runs
8833 .iter()
8834 .enumerate()
8835 .flat_map(move |(ix, (range, highlight_id))| {
8836 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8837 style
8838 } else {
8839 return Default::default();
8840 };
8841 let mut muted_style = style;
8842 muted_style.highlight(fade_out);
8843
8844 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8845 if range.start >= label.filter_range.end {
8846 if range.start > prev_end {
8847 runs.push((prev_end..range.start, fade_out));
8848 }
8849 runs.push((range.clone(), muted_style));
8850 } else if range.end <= label.filter_range.end {
8851 runs.push((range.clone(), style));
8852 } else {
8853 runs.push((range.start..label.filter_range.end, style));
8854 runs.push((label.filter_range.end..range.end, muted_style));
8855 }
8856 prev_end = cmp::max(prev_end, range.end);
8857
8858 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8859 runs.push((prev_end..label.text.len(), fade_out));
8860 }
8861
8862 runs
8863 })
8864}
8865
8866pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8867 let mut index = 0;
8868 let mut codepoints = text.char_indices().peekable();
8869
8870 std::iter::from_fn(move || {
8871 let start_index = index;
8872 while let Some((new_index, codepoint)) = codepoints.next() {
8873 index = new_index + codepoint.len_utf8();
8874 let current_upper = codepoint.is_uppercase();
8875 let next_upper = codepoints
8876 .peek()
8877 .map(|(_, c)| c.is_uppercase())
8878 .unwrap_or(false);
8879
8880 if !current_upper && next_upper {
8881 return Some(&text[start_index..index]);
8882 }
8883 }
8884
8885 index = text.len();
8886 if start_index < text.len() {
8887 return Some(&text[start_index..]);
8888 }
8889 None
8890 })
8891 .flat_map(|word| word.split_inclusive('_'))
8892}
8893
8894trait RangeToAnchorExt {
8895 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8896}
8897
8898impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8899 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8900 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8901 }
8902}