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