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