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