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