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