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