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