1mod blink_manager;
2
3pub mod display_map;
4mod editor_settings;
5mod element;
6mod inlay_cache;
7
8mod git;
9mod highlight_matching_bracket;
10mod hover_popover;
11pub mod items;
12mod link_go_to_definition;
13mod mouse_context_menu;
14pub mod movement;
15pub mod multi_buffer;
16mod persistence;
17pub mod scroll;
18pub mod selections_collection;
19
20#[cfg(test)]
21mod editor_tests;
22#[cfg(any(test, feature = "test-support"))]
23pub mod test;
24
25use ::git::diff::DiffHunk;
26use aho_corasick::AhoCorasick;
27use anyhow::{anyhow, Context, Result};
28use blink_manager::BlinkManager;
29use client::{ClickhouseEvent, TelemetrySettings};
30use clock::ReplicaId;
31use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
32use copilot::Copilot;
33pub use display_map::DisplayPoint;
34use display_map::*;
35pub use editor_settings::EditorSettings;
36pub use element::{
37 Cursor, EditorElement, HighlightedRange, HighlightedRangeLine, LineWithInvisibles,
38};
39use futures::FutureExt;
40use fuzzy::{StringMatch, StringMatchCandidate};
41use gpui::{
42 actions,
43 color::Color,
44 elements::*,
45 executor,
46 fonts::{self, HighlightStyle, TextStyle},
47 geometry::vector::Vector2F,
48 impl_actions,
49 keymap_matcher::KeymapContext,
50 platform::{CursorStyle, MouseButton},
51 serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element,
52 Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
53 WindowContext,
54};
55use highlight_matching_bracket::refresh_matching_bracket_highlights;
56use hover_popover::{hide_hover, HoverState};
57use inlay_cache::{
58 Inlay, InlayCache, InlayId, InlayProperties, InlayRefreshReason, InlaySplice, QueryInlaysRange,
59};
60pub use items::MAX_TAB_TITLE_LEN;
61use itertools::Itertools;
62pub use language::{char_kind, CharKind};
63use language::{
64 language_settings::{self, all_language_settings},
65 AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
66 Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
67 OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
68};
69use link_go_to_definition::{
70 hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
71};
72use log::error;
73use multi_buffer::ToOffsetUtf16;
74pub use multi_buffer::{
75 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
76 ToPoint,
77};
78use ordered_float::OrderedFloat;
79use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
80use scroll::{
81 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
82};
83use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
84use serde::{Deserialize, Serialize};
85use settings::SettingsStore;
86use smallvec::SmallVec;
87use snippet::Snippet;
88use std::{
89 any::TypeId,
90 borrow::Cow,
91 cmp::{self, Ordering, Reverse},
92 mem,
93 num::NonZeroU32,
94 ops::{Deref, DerefMut, Range},
95 path::Path,
96 sync::Arc,
97 time::{Duration, Instant},
98};
99pub use sum_tree::Bias;
100use text::Rope;
101use theme::{DiagnosticStyle, Theme, ThemeSettings};
102use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
103use workspace::{ItemNavHistory, ViewId, Workspace};
104
105use crate::git::diff_hunk_to_display;
106
107const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
108const MAX_LINE_LEN: usize = 1024;
109const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
110const MAX_SELECTION_HISTORY_LEN: usize = 1024;
111const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
112
113pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
114
115#[derive(Clone, Deserialize, PartialEq, Default)]
116pub struct SelectNext {
117 #[serde(default)]
118 pub replace_newest: bool,
119}
120
121#[derive(Clone, Deserialize, PartialEq, Default)]
122pub struct SelectPrevious {
123 #[serde(default)]
124 pub replace_newest: bool,
125}
126
127#[derive(Clone, Deserialize, PartialEq)]
128pub struct SelectToBeginningOfLine {
129 #[serde(default)]
130 stop_at_soft_wraps: bool,
131}
132
133#[derive(Clone, Default, Deserialize, PartialEq)]
134pub struct MovePageUp {
135 #[serde(default)]
136 center_cursor: bool,
137}
138
139#[derive(Clone, Default, Deserialize, PartialEq)]
140pub struct MovePageDown {
141 #[serde(default)]
142 center_cursor: bool,
143}
144
145#[derive(Clone, Deserialize, PartialEq)]
146pub struct SelectToEndOfLine {
147 #[serde(default)]
148 stop_at_soft_wraps: bool,
149}
150
151#[derive(Clone, Deserialize, PartialEq)]
152pub struct ToggleCodeActions {
153 #[serde(default)]
154 pub deployed_from_indicator: bool,
155}
156
157#[derive(Clone, Default, Deserialize, PartialEq)]
158pub struct ConfirmCompletion {
159 #[serde(default)]
160 pub item_ix: Option<usize>,
161}
162
163#[derive(Clone, Default, Deserialize, PartialEq)]
164pub struct ConfirmCodeAction {
165 #[serde(default)]
166 pub item_ix: Option<usize>,
167}
168
169#[derive(Clone, Default, Deserialize, PartialEq)]
170pub struct ToggleComments {
171 #[serde(default)]
172 pub advance_downwards: bool,
173}
174
175#[derive(Clone, Default, Deserialize, PartialEq)]
176pub struct FoldAt {
177 pub buffer_row: u32,
178}
179
180#[derive(Clone, Default, Deserialize, PartialEq)]
181pub struct UnfoldAt {
182 pub buffer_row: u32,
183}
184
185#[derive(Clone, Default, Deserialize, PartialEq)]
186pub struct GutterHover {
187 pub hovered: bool,
188}
189
190actions!(
191 editor,
192 [
193 Cancel,
194 Backspace,
195 Delete,
196 Newline,
197 NewlineAbove,
198 NewlineBelow,
199 GoToDiagnostic,
200 GoToPrevDiagnostic,
201 GoToHunk,
202 GoToPrevHunk,
203 Indent,
204 Outdent,
205 DeleteLine,
206 DeleteToPreviousWordStart,
207 DeleteToPreviousSubwordStart,
208 DeleteToNextWordEnd,
209 DeleteToNextSubwordEnd,
210 DeleteToBeginningOfLine,
211 DeleteToEndOfLine,
212 CutToEndOfLine,
213 DuplicateLine,
214 MoveLineUp,
215 MoveLineDown,
216 JoinLines,
217 Transpose,
218 Cut,
219 Copy,
220 Paste,
221 Undo,
222 Redo,
223 MoveUp,
224 PageUp,
225 MoveDown,
226 PageDown,
227 MoveLeft,
228 MoveRight,
229 MoveToPreviousWordStart,
230 MoveToPreviousSubwordStart,
231 MoveToNextWordEnd,
232 MoveToNextSubwordEnd,
233 MoveToBeginningOfLine,
234 MoveToEndOfLine,
235 MoveToStartOfParagraph,
236 MoveToEndOfParagraph,
237 MoveToBeginning,
238 MoveToEnd,
239 SelectUp,
240 SelectDown,
241 SelectLeft,
242 SelectRight,
243 SelectToPreviousWordStart,
244 SelectToPreviousSubwordStart,
245 SelectToNextWordEnd,
246 SelectToNextSubwordEnd,
247 SelectToStartOfParagraph,
248 SelectToEndOfParagraph,
249 SelectToBeginning,
250 SelectToEnd,
251 SelectAll,
252 SelectLine,
253 SplitSelectionIntoLines,
254 AddSelectionAbove,
255 AddSelectionBelow,
256 Tab,
257 TabPrev,
258 ShowCharacterPalette,
259 SelectLargerSyntaxNode,
260 SelectSmallerSyntaxNode,
261 GoToDefinition,
262 GoToTypeDefinition,
263 MoveToEnclosingBracket,
264 UndoSelection,
265 RedoSelection,
266 FindAllReferences,
267 Rename,
268 ConfirmRename,
269 Fold,
270 UnfoldLines,
271 FoldSelectedRanges,
272 ShowCompletions,
273 OpenExcerpts,
274 RestartLanguageServer,
275 Hover,
276 Format,
277 ToggleSoftWrap,
278 RevealInFinder,
279 CopyPath,
280 CopyRelativePath,
281 CopyHighlightJson
282 ]
283);
284
285impl_actions!(
286 editor,
287 [
288 SelectNext,
289 SelectPrevious,
290 SelectToBeginningOfLine,
291 SelectToEndOfLine,
292 ToggleCodeActions,
293 MovePageUp,
294 MovePageDown,
295 ConfirmCompletion,
296 ConfirmCodeAction,
297 ToggleComments,
298 FoldAt,
299 UnfoldAt,
300 GutterHover
301 ]
302);
303
304enum DocumentHighlightRead {}
305enum DocumentHighlightWrite {}
306enum InputComposition {}
307
308#[derive(Copy, Clone, PartialEq, Eq)]
309pub enum Direction {
310 Prev,
311 Next,
312}
313
314pub fn init_settings(cx: &mut AppContext) {
315 settings::register::<EditorSettings>(cx);
316}
317
318pub fn init(cx: &mut AppContext) {
319 init_settings(cx);
320 cx.add_action(Editor::new_file);
321 cx.add_action(Editor::cancel);
322 cx.add_action(Editor::newline);
323 cx.add_action(Editor::newline_above);
324 cx.add_action(Editor::newline_below);
325 cx.add_action(Editor::backspace);
326 cx.add_action(Editor::delete);
327 cx.add_action(Editor::tab);
328 cx.add_action(Editor::tab_prev);
329 cx.add_action(Editor::indent);
330 cx.add_action(Editor::outdent);
331 cx.add_action(Editor::delete_line);
332 cx.add_action(Editor::join_lines);
333 cx.add_action(Editor::delete_to_previous_word_start);
334 cx.add_action(Editor::delete_to_previous_subword_start);
335 cx.add_action(Editor::delete_to_next_word_end);
336 cx.add_action(Editor::delete_to_next_subword_end);
337 cx.add_action(Editor::delete_to_beginning_of_line);
338 cx.add_action(Editor::delete_to_end_of_line);
339 cx.add_action(Editor::cut_to_end_of_line);
340 cx.add_action(Editor::duplicate_line);
341 cx.add_action(Editor::move_line_up);
342 cx.add_action(Editor::move_line_down);
343 cx.add_action(Editor::transpose);
344 cx.add_action(Editor::cut);
345 cx.add_action(Editor::copy);
346 cx.add_action(Editor::paste);
347 cx.add_action(Editor::undo);
348 cx.add_action(Editor::redo);
349 cx.add_action(Editor::move_up);
350 cx.add_action(Editor::move_page_up);
351 cx.add_action(Editor::move_down);
352 cx.add_action(Editor::move_page_down);
353 cx.add_action(Editor::next_screen);
354 cx.add_action(Editor::move_left);
355 cx.add_action(Editor::move_right);
356 cx.add_action(Editor::move_to_previous_word_start);
357 cx.add_action(Editor::move_to_previous_subword_start);
358 cx.add_action(Editor::move_to_next_word_end);
359 cx.add_action(Editor::move_to_next_subword_end);
360 cx.add_action(Editor::move_to_beginning_of_line);
361 cx.add_action(Editor::move_to_end_of_line);
362 cx.add_action(Editor::move_to_start_of_paragraph);
363 cx.add_action(Editor::move_to_end_of_paragraph);
364 cx.add_action(Editor::move_to_beginning);
365 cx.add_action(Editor::move_to_end);
366 cx.add_action(Editor::select_up);
367 cx.add_action(Editor::select_down);
368 cx.add_action(Editor::select_left);
369 cx.add_action(Editor::select_right);
370 cx.add_action(Editor::select_to_previous_word_start);
371 cx.add_action(Editor::select_to_previous_subword_start);
372 cx.add_action(Editor::select_to_next_word_end);
373 cx.add_action(Editor::select_to_next_subword_end);
374 cx.add_action(Editor::select_to_beginning_of_line);
375 cx.add_action(Editor::select_to_end_of_line);
376 cx.add_action(Editor::select_to_start_of_paragraph);
377 cx.add_action(Editor::select_to_end_of_paragraph);
378 cx.add_action(Editor::select_to_beginning);
379 cx.add_action(Editor::select_to_end);
380 cx.add_action(Editor::select_all);
381 cx.add_action(Editor::select_line);
382 cx.add_action(Editor::split_selection_into_lines);
383 cx.add_action(Editor::add_selection_above);
384 cx.add_action(Editor::add_selection_below);
385 cx.add_action(Editor::select_next);
386 cx.add_action(Editor::select_previous);
387 cx.add_action(Editor::toggle_comments);
388 cx.add_action(Editor::select_larger_syntax_node);
389 cx.add_action(Editor::select_smaller_syntax_node);
390 cx.add_action(Editor::move_to_enclosing_bracket);
391 cx.add_action(Editor::undo_selection);
392 cx.add_action(Editor::redo_selection);
393 cx.add_action(Editor::go_to_diagnostic);
394 cx.add_action(Editor::go_to_prev_diagnostic);
395 cx.add_action(Editor::go_to_hunk);
396 cx.add_action(Editor::go_to_prev_hunk);
397 cx.add_action(Editor::go_to_definition);
398 cx.add_action(Editor::go_to_type_definition);
399 cx.add_action(Editor::fold);
400 cx.add_action(Editor::fold_at);
401 cx.add_action(Editor::unfold_lines);
402 cx.add_action(Editor::unfold_at);
403 cx.add_action(Editor::gutter_hover);
404 cx.add_action(Editor::fold_selected_ranges);
405 cx.add_action(Editor::show_completions);
406 cx.add_action(Editor::toggle_code_actions);
407 cx.add_action(Editor::open_excerpts);
408 cx.add_action(Editor::toggle_soft_wrap);
409 cx.add_action(Editor::reveal_in_finder);
410 cx.add_action(Editor::copy_path);
411 cx.add_action(Editor::copy_relative_path);
412 cx.add_action(Editor::copy_highlight_json);
413 cx.add_async_action(Editor::format);
414 cx.add_action(Editor::restart_language_server);
415 cx.add_action(Editor::show_character_palette);
416 cx.add_async_action(Editor::confirm_completion);
417 cx.add_async_action(Editor::confirm_code_action);
418 cx.add_async_action(Editor::rename);
419 cx.add_async_action(Editor::confirm_rename);
420 cx.add_async_action(Editor::find_all_references);
421 cx.add_action(Editor::next_copilot_suggestion);
422 cx.add_action(Editor::previous_copilot_suggestion);
423 cx.add_action(Editor::copilot_suggest);
424
425 hover_popover::init(cx);
426 scroll::actions::init(cx);
427
428 workspace::register_project_item::<Editor>(cx);
429 workspace::register_followable_item::<Editor>(cx);
430 workspace::register_deserializable_item::<Editor>(cx);
431}
432
433trait InvalidationRegion {
434 fn ranges(&self) -> &[Range<Anchor>];
435}
436
437#[derive(Clone, Debug, PartialEq)]
438pub enum SelectPhase {
439 Begin {
440 position: DisplayPoint,
441 add: bool,
442 click_count: usize,
443 },
444 BeginColumnar {
445 position: DisplayPoint,
446 goal_column: u32,
447 },
448 Extend {
449 position: DisplayPoint,
450 click_count: usize,
451 },
452 Update {
453 position: DisplayPoint,
454 goal_column: u32,
455 scroll_position: Vector2F,
456 },
457 End,
458}
459
460#[derive(Clone, Debug)]
461pub enum SelectMode {
462 Character,
463 Word(Range<Anchor>),
464 Line(Range<Anchor>),
465 All,
466}
467
468#[derive(Copy, Clone, PartialEq, Eq, Debug)]
469pub enum EditorMode {
470 SingleLine,
471 AutoHeight { max_lines: usize },
472 Full,
473}
474
475#[derive(Clone, Debug)]
476pub enum SoftWrap {
477 None,
478 EditorWidth,
479 Column(u32),
480}
481
482#[derive(Clone)]
483pub struct EditorStyle {
484 pub text: TextStyle,
485 pub placeholder_text: Option<TextStyle>,
486 pub theme: theme::Editor,
487 pub theme_id: usize,
488}
489
490type CompletionId = usize;
491
492type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
493type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
494
495pub struct Editor {
496 handle: WeakViewHandle<Self>,
497 buffer: ModelHandle<MultiBuffer>,
498 display_map: ModelHandle<DisplayMap>,
499 pub selections: SelectionsCollection,
500 pub scroll_manager: ScrollManager,
501 columnar_selection_tail: Option<Anchor>,
502 add_selections_state: Option<AddSelectionsState>,
503 select_next_state: Option<SelectNextState>,
504 select_prev_state: Option<SelectNextState>,
505 selection_history: SelectionHistory,
506 autoclose_regions: Vec<AutocloseRegion>,
507 snippet_stack: InvalidationStack<SnippetState>,
508 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
509 ime_transaction: Option<TransactionId>,
510 active_diagnostics: Option<ActiveDiagnosticGroup>,
511 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
512 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
513 override_text_style: Option<Box<OverrideTextStyle>>,
514 project: Option<ModelHandle<Project>>,
515 focused: bool,
516 blink_manager: ModelHandle<BlinkManager>,
517 show_local_selections: bool,
518 mode: EditorMode,
519 show_gutter: bool,
520 placeholder_text: Option<Arc<str>>,
521 highlighted_rows: Option<Range<u32>>,
522 #[allow(clippy::type_complexity)]
523 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
524 nav_history: Option<ItemNavHistory>,
525 context_menu: Option<ContextMenu>,
526 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
527 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
528 next_completion_id: CompletionId,
529 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
530 code_actions_task: Option<Task<()>>,
531 document_highlights_task: Option<Task<()>>,
532 pending_rename: Option<RenameState>,
533 searchable: bool,
534 cursor_shape: CursorShape,
535 workspace: Option<(WeakViewHandle<Workspace>, i64)>,
536 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
537 input_enabled: bool,
538 read_only: bool,
539 leader_replica_id: Option<u16>,
540 remote_id: Option<ViewId>,
541 hover_state: HoverState,
542 gutter_hovered: bool,
543 link_go_to_definition_state: LinkGoToDefinitionState,
544 copilot_state: CopilotState,
545 inlay_cache: InlayCache,
546 _subscriptions: Vec<Subscription>,
547}
548
549pub struct EditorSnapshot {
550 pub mode: EditorMode,
551 pub show_gutter: bool,
552 pub display_snapshot: DisplaySnapshot,
553 pub placeholder_text: Option<Arc<str>>,
554 is_focused: bool,
555 scroll_anchor: ScrollAnchor,
556 ongoing_scroll: OngoingScroll,
557}
558
559#[derive(Clone, Debug)]
560struct SelectionHistoryEntry {
561 selections: Arc<[Selection<Anchor>]>,
562 select_next_state: Option<SelectNextState>,
563 select_prev_state: Option<SelectNextState>,
564 add_selections_state: Option<AddSelectionsState>,
565}
566
567enum SelectionHistoryMode {
568 Normal,
569 Undoing,
570 Redoing,
571}
572
573impl Default for SelectionHistoryMode {
574 fn default() -> Self {
575 Self::Normal
576 }
577}
578
579#[derive(Default)]
580struct SelectionHistory {
581 #[allow(clippy::type_complexity)]
582 selections_by_transaction:
583 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
584 mode: SelectionHistoryMode,
585 undo_stack: VecDeque<SelectionHistoryEntry>,
586 redo_stack: VecDeque<SelectionHistoryEntry>,
587}
588
589impl SelectionHistory {
590 fn insert_transaction(
591 &mut self,
592 transaction_id: TransactionId,
593 selections: Arc<[Selection<Anchor>]>,
594 ) {
595 self.selections_by_transaction
596 .insert(transaction_id, (selections, None));
597 }
598
599 #[allow(clippy::type_complexity)]
600 fn transaction(
601 &self,
602 transaction_id: TransactionId,
603 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
604 self.selections_by_transaction.get(&transaction_id)
605 }
606
607 #[allow(clippy::type_complexity)]
608 fn transaction_mut(
609 &mut self,
610 transaction_id: TransactionId,
611 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
612 self.selections_by_transaction.get_mut(&transaction_id)
613 }
614
615 fn push(&mut self, entry: SelectionHistoryEntry) {
616 if !entry.selections.is_empty() {
617 match self.mode {
618 SelectionHistoryMode::Normal => {
619 self.push_undo(entry);
620 self.redo_stack.clear();
621 }
622 SelectionHistoryMode::Undoing => self.push_redo(entry),
623 SelectionHistoryMode::Redoing => self.push_undo(entry),
624 }
625 }
626 }
627
628 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
629 if self
630 .undo_stack
631 .back()
632 .map_or(true, |e| e.selections != entry.selections)
633 {
634 self.undo_stack.push_back(entry);
635 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
636 self.undo_stack.pop_front();
637 }
638 }
639 }
640
641 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
642 if self
643 .redo_stack
644 .back()
645 .map_or(true, |e| e.selections != entry.selections)
646 {
647 self.redo_stack.push_back(entry);
648 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
649 self.redo_stack.pop_front();
650 }
651 }
652 }
653}
654
655#[derive(Clone, Debug)]
656struct AddSelectionsState {
657 above: bool,
658 stack: Vec<usize>,
659}
660
661#[derive(Clone, Debug)]
662struct SelectNextState {
663 query: AhoCorasick,
664 wordwise: bool,
665 done: bool,
666}
667
668#[derive(Debug)]
669struct AutocloseRegion {
670 selection_id: usize,
671 range: Range<Anchor>,
672 pair: BracketPair,
673}
674
675#[derive(Debug)]
676struct SnippetState {
677 ranges: Vec<Vec<Range<Anchor>>>,
678 active_index: usize,
679}
680
681pub struct RenameState {
682 pub range: Range<Anchor>,
683 pub old_name: Arc<str>,
684 pub editor: ViewHandle<Editor>,
685 block_id: BlockId,
686}
687
688struct InvalidationStack<T>(Vec<T>);
689
690enum ContextMenu {
691 Completions(CompletionsMenu),
692 CodeActions(CodeActionsMenu),
693}
694
695impl ContextMenu {
696 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
697 if self.visible() {
698 match self {
699 ContextMenu::Completions(menu) => menu.select_first(cx),
700 ContextMenu::CodeActions(menu) => menu.select_first(cx),
701 }
702 true
703 } else {
704 false
705 }
706 }
707
708 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
709 if self.visible() {
710 match self {
711 ContextMenu::Completions(menu) => menu.select_prev(cx),
712 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
713 }
714 true
715 } else {
716 false
717 }
718 }
719
720 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
721 if self.visible() {
722 match self {
723 ContextMenu::Completions(menu) => menu.select_next(cx),
724 ContextMenu::CodeActions(menu) => menu.select_next(cx),
725 }
726 true
727 } else {
728 false
729 }
730 }
731
732 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
733 if self.visible() {
734 match self {
735 ContextMenu::Completions(menu) => menu.select_last(cx),
736 ContextMenu::CodeActions(menu) => menu.select_last(cx),
737 }
738 true
739 } else {
740 false
741 }
742 }
743
744 fn visible(&self) -> bool {
745 match self {
746 ContextMenu::Completions(menu) => menu.visible(),
747 ContextMenu::CodeActions(menu) => menu.visible(),
748 }
749 }
750
751 fn render(
752 &self,
753 cursor_position: DisplayPoint,
754 style: EditorStyle,
755 cx: &mut ViewContext<Editor>,
756 ) -> (DisplayPoint, AnyElement<Editor>) {
757 match self {
758 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
759 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
760 }
761 }
762}
763
764struct CompletionsMenu {
765 id: CompletionId,
766 initial_position: Anchor,
767 buffer: ModelHandle<Buffer>,
768 completions: Arc<[Completion]>,
769 match_candidates: Vec<StringMatchCandidate>,
770 matches: Arc<[StringMatch]>,
771 selected_item: usize,
772 list: UniformListState,
773}
774
775impl CompletionsMenu {
776 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
777 self.selected_item = 0;
778 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
779 cx.notify();
780 }
781
782 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
783 if self.selected_item > 0 {
784 self.selected_item -= 1;
785 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
786 }
787 cx.notify();
788 }
789
790 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
791 if self.selected_item + 1 < self.matches.len() {
792 self.selected_item += 1;
793 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
794 }
795 cx.notify();
796 }
797
798 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
799 self.selected_item = self.matches.len() - 1;
800 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
801 cx.notify();
802 }
803
804 fn visible(&self) -> bool {
805 !self.matches.is_empty()
806 }
807
808 fn render(&self, style: EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
809 enum CompletionTag {}
810
811 let completions = self.completions.clone();
812 let matches = self.matches.clone();
813 let selected_item = self.selected_item;
814 let container_style = style.autocomplete.container;
815 UniformList::new(
816 self.list.clone(),
817 matches.len(),
818 cx,
819 move |_, range, items, cx| {
820 let start_ix = range.start;
821 for (ix, mat) in matches[range].iter().enumerate() {
822 let completion = &completions[mat.candidate_id];
823 let item_ix = start_ix + ix;
824 items.push(
825 MouseEventHandler::<CompletionTag, _>::new(
826 mat.candidate_id,
827 cx,
828 |state, _| {
829 let item_style = if item_ix == selected_item {
830 style.autocomplete.selected_item
831 } else if state.hovered() {
832 style.autocomplete.hovered_item
833 } else {
834 style.autocomplete.item
835 };
836
837 Text::new(completion.label.text.clone(), style.text.clone())
838 .with_soft_wrap(false)
839 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
840 &completion.label.text,
841 style.text.color.into(),
842 styled_runs_for_code_label(
843 &completion.label,
844 &style.syntax,
845 ),
846 &mat.positions,
847 ))
848 .contained()
849 .with_style(item_style)
850 },
851 )
852 .with_cursor_style(CursorStyle::PointingHand)
853 .on_down(MouseButton::Left, move |_, this, cx| {
854 this.confirm_completion(
855 &ConfirmCompletion {
856 item_ix: Some(item_ix),
857 },
858 cx,
859 );
860 })
861 .into_any(),
862 );
863 }
864 },
865 )
866 .with_width_from_item(
867 self.matches
868 .iter()
869 .enumerate()
870 .max_by_key(|(_, mat)| {
871 self.completions[mat.candidate_id]
872 .label
873 .text
874 .chars()
875 .count()
876 })
877 .map(|(ix, _)| ix),
878 )
879 .contained()
880 .with_style(container_style)
881 .into_any()
882 }
883
884 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
885 let mut matches = if let Some(query) = query {
886 fuzzy::match_strings(
887 &self.match_candidates,
888 query,
889 query.chars().any(|c| c.is_uppercase()),
890 100,
891 &Default::default(),
892 executor,
893 )
894 .await
895 } else {
896 self.match_candidates
897 .iter()
898 .enumerate()
899 .map(|(candidate_id, candidate)| StringMatch {
900 candidate_id,
901 score: Default::default(),
902 positions: Default::default(),
903 string: candidate.string.clone(),
904 })
905 .collect()
906 };
907
908 //Remove all candidates where the query's start does not match the start of any word in the candidate
909 if let Some(query) = query {
910 if let Some(query_start) = query.chars().next() {
911 matches.retain(|string_match| {
912 split_words(&string_match.string).any(|word| {
913 //Check that the first codepoint of the word as lowercase matches the first
914 //codepoint of the query as lowercase
915 word.chars()
916 .flat_map(|codepoint| codepoint.to_lowercase())
917 .zip(query_start.to_lowercase())
918 .all(|(word_cp, query_cp)| word_cp == query_cp)
919 })
920 });
921 }
922 }
923
924 matches.sort_unstable_by_key(|mat| {
925 let completion = &self.completions[mat.candidate_id];
926 (
927 completion.lsp_completion.sort_text.as_ref(),
928 Reverse(OrderedFloat(mat.score)),
929 completion.sort_key(),
930 )
931 });
932
933 for mat in &mut matches {
934 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
935 for position in &mut mat.positions {
936 *position += filter_start;
937 }
938 }
939
940 self.matches = matches.into();
941 }
942}
943
944#[derive(Clone)]
945struct CodeActionsMenu {
946 actions: Arc<[CodeAction]>,
947 buffer: ModelHandle<Buffer>,
948 selected_item: usize,
949 list: UniformListState,
950 deployed_from_indicator: bool,
951}
952
953impl CodeActionsMenu {
954 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
955 self.selected_item = 0;
956 cx.notify()
957 }
958
959 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
960 if self.selected_item > 0 {
961 self.selected_item -= 1;
962 cx.notify()
963 }
964 }
965
966 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
967 if self.selected_item + 1 < self.actions.len() {
968 self.selected_item += 1;
969 cx.notify()
970 }
971 }
972
973 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
974 self.selected_item = self.actions.len() - 1;
975 cx.notify()
976 }
977
978 fn visible(&self) -> bool {
979 !self.actions.is_empty()
980 }
981
982 fn render(
983 &self,
984 mut cursor_position: DisplayPoint,
985 style: EditorStyle,
986 cx: &mut ViewContext<Editor>,
987 ) -> (DisplayPoint, AnyElement<Editor>) {
988 enum ActionTag {}
989
990 let container_style = style.autocomplete.container;
991 let actions = self.actions.clone();
992 let selected_item = self.selected_item;
993 let element = UniformList::new(
994 self.list.clone(),
995 actions.len(),
996 cx,
997 move |_, range, items, cx| {
998 let start_ix = range.start;
999 for (ix, action) in actions[range].iter().enumerate() {
1000 let item_ix = start_ix + ix;
1001 items.push(
1002 MouseEventHandler::<ActionTag, _>::new(item_ix, cx, |state, _| {
1003 let item_style = if item_ix == selected_item {
1004 style.autocomplete.selected_item
1005 } else if state.hovered() {
1006 style.autocomplete.hovered_item
1007 } else {
1008 style.autocomplete.item
1009 };
1010
1011 Text::new(action.lsp_action.title.clone(), style.text.clone())
1012 .with_soft_wrap(false)
1013 .contained()
1014 .with_style(item_style)
1015 })
1016 .with_cursor_style(CursorStyle::PointingHand)
1017 .on_down(MouseButton::Left, move |_, this, cx| {
1018 let workspace = this
1019 .workspace
1020 .as_ref()
1021 .and_then(|(workspace, _)| workspace.upgrade(cx));
1022 cx.window_context().defer(move |cx| {
1023 if let Some(workspace) = workspace {
1024 workspace.update(cx, |workspace, cx| {
1025 if let Some(task) = Editor::confirm_code_action(
1026 workspace,
1027 &Default::default(),
1028 cx,
1029 ) {
1030 task.detach_and_log_err(cx);
1031 }
1032 });
1033 }
1034 });
1035 })
1036 .into_any(),
1037 );
1038 }
1039 },
1040 )
1041 .with_width_from_item(
1042 self.actions
1043 .iter()
1044 .enumerate()
1045 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1046 .map(|(ix, _)| ix),
1047 )
1048 .contained()
1049 .with_style(container_style)
1050 .into_any();
1051
1052 if self.deployed_from_indicator {
1053 *cursor_position.column_mut() = 0;
1054 }
1055
1056 (cursor_position, element)
1057 }
1058}
1059
1060pub struct CopilotState {
1061 excerpt_id: Option<ExcerptId>,
1062 pending_refresh: Task<Option<()>>,
1063 pending_cycling_refresh: Task<Option<()>>,
1064 cycled: bool,
1065 completions: Vec<copilot::Completion>,
1066 active_completion_index: usize,
1067 suggestion: Option<Inlay>,
1068}
1069
1070impl Default for CopilotState {
1071 fn default() -> Self {
1072 Self {
1073 excerpt_id: None,
1074 pending_cycling_refresh: Task::ready(Some(())),
1075 pending_refresh: Task::ready(Some(())),
1076 completions: Default::default(),
1077 active_completion_index: 0,
1078 cycled: false,
1079 suggestion: None,
1080 }
1081 }
1082}
1083
1084impl CopilotState {
1085 fn active_completion(&self) -> Option<&copilot::Completion> {
1086 self.completions.get(self.active_completion_index)
1087 }
1088
1089 fn text_for_active_completion(
1090 &self,
1091 cursor: Anchor,
1092 buffer: &MultiBufferSnapshot,
1093 ) -> Option<&str> {
1094 use language::ToOffset as _;
1095
1096 let completion = self.active_completion()?;
1097 let excerpt_id = self.excerpt_id?;
1098 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1099 if excerpt_id != cursor.excerpt_id
1100 || !completion.range.start.is_valid(completion_buffer)
1101 || !completion.range.end.is_valid(completion_buffer)
1102 {
1103 return None;
1104 }
1105
1106 let mut completion_range = completion.range.to_offset(&completion_buffer);
1107 let prefix_len = Self::common_prefix(
1108 completion_buffer.chars_for_range(completion_range.clone()),
1109 completion.text.chars(),
1110 );
1111 completion_range.start += prefix_len;
1112 let suffix_len = Self::common_prefix(
1113 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1114 completion.text[prefix_len..].chars().rev(),
1115 );
1116 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1117
1118 if completion_range.is_empty()
1119 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1120 {
1121 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1122 } else {
1123 None
1124 }
1125 }
1126
1127 fn cycle_completions(&mut self, direction: Direction) {
1128 match direction {
1129 Direction::Prev => {
1130 self.active_completion_index = if self.active_completion_index == 0 {
1131 self.completions.len().saturating_sub(1)
1132 } else {
1133 self.active_completion_index - 1
1134 };
1135 }
1136 Direction::Next => {
1137 if self.completions.len() == 0 {
1138 self.active_completion_index = 0
1139 } else {
1140 self.active_completion_index =
1141 (self.active_completion_index + 1) % self.completions.len();
1142 }
1143 }
1144 }
1145 }
1146
1147 fn push_completion(&mut self, new_completion: copilot::Completion) {
1148 for completion in &self.completions {
1149 if completion.text == new_completion.text && completion.range == new_completion.range {
1150 return;
1151 }
1152 }
1153 self.completions.push(new_completion);
1154 }
1155
1156 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1157 a.zip(b)
1158 .take_while(|(a, b)| a == b)
1159 .map(|(a, _)| a.len_utf8())
1160 .sum()
1161 }
1162}
1163
1164#[derive(Debug)]
1165struct ActiveDiagnosticGroup {
1166 primary_range: Range<Anchor>,
1167 primary_message: String,
1168 blocks: HashMap<BlockId, Diagnostic>,
1169 is_valid: bool,
1170}
1171
1172#[derive(Serialize, Deserialize)]
1173pub struct ClipboardSelection {
1174 pub len: usize,
1175 pub is_entire_line: bool,
1176 pub first_line_indent: u32,
1177}
1178
1179#[derive(Debug)]
1180pub struct NavigationData {
1181 cursor_anchor: Anchor,
1182 cursor_position: Point,
1183 scroll_anchor: ScrollAnchor,
1184 scroll_top_row: u32,
1185}
1186
1187pub struct EditorCreated(pub ViewHandle<Editor>);
1188
1189enum GotoDefinitionKind {
1190 Symbol,
1191 Type,
1192}
1193
1194impl Editor {
1195 pub fn single_line(
1196 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1197 cx: &mut ViewContext<Self>,
1198 ) -> Self {
1199 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1200 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1201 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
1202 }
1203
1204 pub fn multi_line(
1205 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1206 cx: &mut ViewContext<Self>,
1207 ) -> Self {
1208 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1209 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1210 Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1211 }
1212
1213 pub fn auto_height(
1214 max_lines: usize,
1215 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1216 cx: &mut ViewContext<Self>,
1217 ) -> Self {
1218 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1219 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1220 Self::new(
1221 EditorMode::AutoHeight { max_lines },
1222 buffer,
1223 None,
1224 field_editor_style,
1225 cx,
1226 )
1227 }
1228
1229 pub fn for_buffer(
1230 buffer: ModelHandle<Buffer>,
1231 project: Option<ModelHandle<Project>>,
1232 cx: &mut ViewContext<Self>,
1233 ) -> Self {
1234 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1235 Self::new(EditorMode::Full, buffer, project, None, cx)
1236 }
1237
1238 pub fn for_multibuffer(
1239 buffer: ModelHandle<MultiBuffer>,
1240 project: Option<ModelHandle<Project>>,
1241 cx: &mut ViewContext<Self>,
1242 ) -> Self {
1243 Self::new(EditorMode::Full, buffer, project, None, cx)
1244 }
1245
1246 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1247 let mut clone = Self::new(
1248 self.mode,
1249 self.buffer.clone(),
1250 self.project.clone(),
1251 self.get_field_editor_theme.clone(),
1252 cx,
1253 );
1254 self.display_map.update(cx, |display_map, cx| {
1255 let snapshot = display_map.snapshot(cx);
1256 clone.display_map.update(cx, |display_map, cx| {
1257 display_map.set_state(&snapshot, cx);
1258 });
1259 });
1260 clone.selections.clone_state(&self.selections);
1261 clone.scroll_manager.clone_state(&self.scroll_manager);
1262 clone.searchable = self.searchable;
1263 clone
1264 }
1265
1266 fn new(
1267 mode: EditorMode,
1268 buffer: ModelHandle<MultiBuffer>,
1269 project: Option<ModelHandle<Project>>,
1270 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1271 cx: &mut ViewContext<Self>,
1272 ) -> Self {
1273 let editor_view_id = cx.view_id();
1274 let display_map = cx.add_model(|cx| {
1275 let settings = settings::get::<ThemeSettings>(cx);
1276 let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
1277 DisplayMap::new(
1278 buffer.clone(),
1279 style.text.font_id,
1280 style.text.font_size,
1281 None,
1282 2,
1283 1,
1284 cx,
1285 )
1286 });
1287
1288 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1289
1290 let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1291
1292 let soft_wrap_mode_override =
1293 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1294
1295 let mut project_subscriptions = Vec::new();
1296 if mode == EditorMode::Full {
1297 if let Some(project) = project.as_ref() {
1298 if buffer.read(cx).is_singleton() {
1299 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1300 cx.emit(Event::TitleChanged);
1301 }));
1302 }
1303 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1304 if let project::Event::RefreshInlays = event {
1305 editor.refresh_inlays(InlayRefreshReason::Regular, cx);
1306 };
1307 }));
1308 }
1309 }
1310
1311 let mut this = Self {
1312 handle: cx.weak_handle(),
1313 buffer: buffer.clone(),
1314 display_map: display_map.clone(),
1315 selections,
1316 scroll_manager: ScrollManager::new(),
1317 columnar_selection_tail: None,
1318 add_selections_state: None,
1319 select_next_state: None,
1320 select_prev_state: None,
1321 selection_history: Default::default(),
1322 autoclose_regions: Default::default(),
1323 snippet_stack: Default::default(),
1324 select_larger_syntax_node_stack: Vec::new(),
1325 ime_transaction: Default::default(),
1326 active_diagnostics: None,
1327 soft_wrap_mode_override,
1328 get_field_editor_theme,
1329 project,
1330 focused: false,
1331 blink_manager: blink_manager.clone(),
1332 show_local_selections: true,
1333 mode,
1334 show_gutter: mode == EditorMode::Full,
1335 placeholder_text: None,
1336 highlighted_rows: None,
1337 background_highlights: Default::default(),
1338 nav_history: None,
1339 context_menu: None,
1340 mouse_context_menu: cx
1341 .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
1342 completion_tasks: Default::default(),
1343 next_completion_id: 0,
1344 available_code_actions: Default::default(),
1345 code_actions_task: Default::default(),
1346 document_highlights_task: Default::default(),
1347 pending_rename: Default::default(),
1348 searchable: true,
1349 override_text_style: None,
1350 cursor_shape: Default::default(),
1351 workspace: None,
1352 keymap_context_layers: Default::default(),
1353 input_enabled: true,
1354 read_only: false,
1355 leader_replica_id: None,
1356 remote_id: None,
1357 hover_state: Default::default(),
1358 link_go_to_definition_state: Default::default(),
1359 copilot_state: Default::default(),
1360 // TODO kb has to live between editor reopens
1361 inlay_cache: InlayCache::new(settings::get::<EditorSettings>(cx).inlay_hints),
1362 gutter_hovered: false,
1363 _subscriptions: vec![
1364 cx.observe(&buffer, Self::on_buffer_changed),
1365 cx.subscribe(&buffer, Self::on_buffer_event),
1366 cx.observe(&display_map, Self::on_display_map_changed),
1367 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1368 cx.observe_global::<SettingsStore, _>(Self::settings_changed),
1369 ],
1370 };
1371
1372 this._subscriptions.extend(project_subscriptions);
1373
1374 this.end_selection(cx);
1375 this.scroll_manager.show_scrollbar(cx);
1376
1377 let editor_created_event = EditorCreated(cx.handle());
1378 cx.emit_global(editor_created_event);
1379
1380 if mode == EditorMode::Full {
1381 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1382 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1383 }
1384
1385 this.report_editor_event("open", None, cx);
1386 this.refresh_inlays(InlayRefreshReason::Regular, cx);
1387 this
1388 }
1389
1390 pub fn new_file(
1391 workspace: &mut Workspace,
1392 _: &workspace::NewFile,
1393 cx: &mut ViewContext<Workspace>,
1394 ) {
1395 let project = workspace.project().clone();
1396 if project.read(cx).is_remote() {
1397 cx.propagate_action();
1398 } else if let Some(buffer) = project
1399 .update(cx, |project, cx| project.create_buffer("", None, cx))
1400 .log_err()
1401 {
1402 workspace.add_item(
1403 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1404 cx,
1405 );
1406 }
1407 }
1408
1409 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1410 self.buffer.read(cx).replica_id()
1411 }
1412
1413 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1414 self.leader_replica_id
1415 }
1416
1417 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1418 &self.buffer
1419 }
1420
1421 fn workspace(&self, cx: &AppContext) -> Option<ViewHandle<Workspace>> {
1422 self.workspace.as_ref()?.0.upgrade(cx)
1423 }
1424
1425 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1426 self.buffer().read(cx).title(cx)
1427 }
1428
1429 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1430 EditorSnapshot {
1431 mode: self.mode,
1432 show_gutter: self.show_gutter,
1433 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1434 scroll_anchor: self.scroll_manager.anchor(),
1435 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1436 placeholder_text: self.placeholder_text.clone(),
1437 is_focused: self
1438 .handle
1439 .upgrade(cx)
1440 .map_or(false, |handle| handle.is_focused(cx)),
1441 }
1442 }
1443
1444 pub fn language_at<'a, T: ToOffset>(
1445 &self,
1446 point: T,
1447 cx: &'a AppContext,
1448 ) -> Option<Arc<Language>> {
1449 self.buffer.read(cx).language_at(point, cx)
1450 }
1451
1452 pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> {
1453 self.buffer.read(cx).read(cx).file_at(point).cloned()
1454 }
1455
1456 pub fn active_excerpt(
1457 &self,
1458 cx: &AppContext,
1459 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1460 self.buffer
1461 .read(cx)
1462 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1463 }
1464
1465 fn style(&self, cx: &AppContext) -> EditorStyle {
1466 build_style(
1467 settings::get::<ThemeSettings>(cx),
1468 self.get_field_editor_theme.as_deref(),
1469 self.override_text_style.as_deref(),
1470 cx,
1471 )
1472 }
1473
1474 pub fn mode(&self) -> EditorMode {
1475 self.mode
1476 }
1477
1478 pub fn set_placeholder_text(
1479 &mut self,
1480 placeholder_text: impl Into<Arc<str>>,
1481 cx: &mut ViewContext<Self>,
1482 ) {
1483 self.placeholder_text = Some(placeholder_text.into());
1484 cx.notify();
1485 }
1486
1487 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1488 self.cursor_shape = cursor_shape;
1489 cx.notify();
1490 }
1491
1492 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1493 if self.display_map.read(cx).clip_at_line_ends != clip {
1494 self.display_map
1495 .update(cx, |map, _| map.clip_at_line_ends = clip);
1496 }
1497 }
1498
1499 pub fn set_keymap_context_layer<Tag: 'static>(
1500 &mut self,
1501 context: KeymapContext,
1502 cx: &mut ViewContext<Self>,
1503 ) {
1504 self.keymap_context_layers
1505 .insert(TypeId::of::<Tag>(), context);
1506 cx.notify();
1507 }
1508
1509 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1510 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1511 cx.notify();
1512 }
1513
1514 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1515 self.input_enabled = input_enabled;
1516 }
1517
1518 pub fn set_read_only(&mut self, read_only: bool) {
1519 self.read_only = read_only;
1520 }
1521
1522 fn selections_did_change(
1523 &mut self,
1524 local: bool,
1525 old_cursor_position: &Anchor,
1526 cx: &mut ViewContext<Self>,
1527 ) {
1528 if self.focused && self.leader_replica_id.is_none() {
1529 self.buffer.update(cx, |buffer, cx| {
1530 buffer.set_active_selections(
1531 &self.selections.disjoint_anchors(),
1532 self.selections.line_mode,
1533 self.cursor_shape,
1534 cx,
1535 )
1536 });
1537 }
1538
1539 let display_map = self
1540 .display_map
1541 .update(cx, |display_map, cx| display_map.snapshot(cx));
1542 let buffer = &display_map.buffer_snapshot;
1543 self.add_selections_state = None;
1544 self.select_next_state = None;
1545 self.select_prev_state = None;
1546 self.select_larger_syntax_node_stack.clear();
1547 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1548 self.snippet_stack
1549 .invalidate(&self.selections.disjoint_anchors(), buffer);
1550 self.take_rename(false, cx);
1551
1552 let new_cursor_position = self.selections.newest_anchor().head();
1553
1554 self.push_to_nav_history(
1555 old_cursor_position.clone(),
1556 Some(new_cursor_position.to_point(buffer)),
1557 cx,
1558 );
1559
1560 if local {
1561 let new_cursor_position = self.selections.newest_anchor().head();
1562 let completion_menu = match self.context_menu.as_mut() {
1563 Some(ContextMenu::Completions(menu)) => Some(menu),
1564 _ => {
1565 self.context_menu.take();
1566 None
1567 }
1568 };
1569
1570 if let Some(completion_menu) = completion_menu {
1571 let cursor_position = new_cursor_position.to_offset(buffer);
1572 let (word_range, kind) =
1573 buffer.surrounding_word(completion_menu.initial_position.clone());
1574 if kind == Some(CharKind::Word)
1575 && word_range.to_inclusive().contains(&cursor_position)
1576 {
1577 let query = Self::completion_query(buffer, cursor_position);
1578 cx.background()
1579 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1580 self.show_completions(&ShowCompletions, cx);
1581 } else {
1582 self.hide_context_menu(cx);
1583 }
1584 }
1585
1586 hide_hover(self, cx);
1587
1588 if old_cursor_position.to_display_point(&display_map).row()
1589 != new_cursor_position.to_display_point(&display_map).row()
1590 {
1591 self.available_code_actions.take();
1592 }
1593 self.refresh_code_actions(cx);
1594 self.refresh_document_highlights(cx);
1595 refresh_matching_bracket_highlights(self, cx);
1596 self.discard_copilot_suggestion(cx);
1597 }
1598
1599 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1600 cx.emit(Event::SelectionsChanged { local });
1601 cx.notify();
1602 }
1603
1604 pub fn change_selections<R>(
1605 &mut self,
1606 autoscroll: Option<Autoscroll>,
1607 cx: &mut ViewContext<Self>,
1608 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1609 ) -> R {
1610 let old_cursor_position = self.selections.newest_anchor().head();
1611 self.push_to_selection_history();
1612
1613 let (changed, result) = self.selections.change_with(cx, change);
1614
1615 if changed {
1616 if let Some(autoscroll) = autoscroll {
1617 self.request_autoscroll(autoscroll, cx);
1618 }
1619 self.selections_did_change(true, &old_cursor_position, cx);
1620 }
1621
1622 result
1623 }
1624
1625 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1626 where
1627 I: IntoIterator<Item = (Range<S>, T)>,
1628 S: ToOffset,
1629 T: Into<Arc<str>>,
1630 {
1631 if self.read_only {
1632 return;
1633 }
1634
1635 self.buffer
1636 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1637 }
1638
1639 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1640 where
1641 I: IntoIterator<Item = (Range<S>, T)>,
1642 S: ToOffset,
1643 T: Into<Arc<str>>,
1644 {
1645 if self.read_only {
1646 return;
1647 }
1648
1649 self.buffer.update(cx, |buffer, cx| {
1650 buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
1651 });
1652 }
1653
1654 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
1655 self.hide_context_menu(cx);
1656
1657 match phase {
1658 SelectPhase::Begin {
1659 position,
1660 add,
1661 click_count,
1662 } => self.begin_selection(position, add, click_count, cx),
1663 SelectPhase::BeginColumnar {
1664 position,
1665 goal_column,
1666 } => self.begin_columnar_selection(position, goal_column, cx),
1667 SelectPhase::Extend {
1668 position,
1669 click_count,
1670 } => self.extend_selection(position, click_count, cx),
1671 SelectPhase::Update {
1672 position,
1673 goal_column,
1674 scroll_position,
1675 } => self.update_selection(position, goal_column, scroll_position, cx),
1676 SelectPhase::End => self.end_selection(cx),
1677 }
1678 }
1679
1680 fn extend_selection(
1681 &mut self,
1682 position: DisplayPoint,
1683 click_count: usize,
1684 cx: &mut ViewContext<Self>,
1685 ) {
1686 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1687 let tail = self.selections.newest::<usize>(cx).tail();
1688 self.begin_selection(position, false, click_count, cx);
1689
1690 let position = position.to_offset(&display_map, Bias::Left);
1691 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1692
1693 let mut pending_selection = self
1694 .selections
1695 .pending_anchor()
1696 .expect("extend_selection not called with pending selection");
1697 if position >= tail {
1698 pending_selection.start = tail_anchor;
1699 } else {
1700 pending_selection.end = tail_anchor;
1701 pending_selection.reversed = true;
1702 }
1703
1704 let mut pending_mode = self.selections.pending_mode().unwrap();
1705 match &mut pending_mode {
1706 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
1707 _ => {}
1708 }
1709
1710 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
1711 s.set_pending(pending_selection, pending_mode)
1712 });
1713 }
1714
1715 fn begin_selection(
1716 &mut self,
1717 position: DisplayPoint,
1718 add: bool,
1719 click_count: usize,
1720 cx: &mut ViewContext<Self>,
1721 ) {
1722 if !self.focused {
1723 cx.focus_self();
1724 }
1725
1726 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1727 let buffer = &display_map.buffer_snapshot;
1728 let newest_selection = self.selections.newest_anchor().clone();
1729 let position = display_map.clip_point(position, Bias::Left);
1730
1731 let start;
1732 let end;
1733 let mode;
1734 let auto_scroll;
1735 match click_count {
1736 1 => {
1737 start = buffer.anchor_before(position.to_point(&display_map));
1738 end = start.clone();
1739 mode = SelectMode::Character;
1740 auto_scroll = true;
1741 }
1742 2 => {
1743 let range = movement::surrounding_word(&display_map, position);
1744 start = buffer.anchor_before(range.start.to_point(&display_map));
1745 end = buffer.anchor_before(range.end.to_point(&display_map));
1746 mode = SelectMode::Word(start.clone()..end.clone());
1747 auto_scroll = true;
1748 }
1749 3 => {
1750 let position = display_map
1751 .clip_point(position, Bias::Left)
1752 .to_point(&display_map);
1753 let line_start = display_map.prev_line_boundary(position).0;
1754 let next_line_start = buffer.clip_point(
1755 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1756 Bias::Left,
1757 );
1758 start = buffer.anchor_before(line_start);
1759 end = buffer.anchor_before(next_line_start);
1760 mode = SelectMode::Line(start.clone()..end.clone());
1761 auto_scroll = true;
1762 }
1763 _ => {
1764 start = buffer.anchor_before(0);
1765 end = buffer.anchor_before(buffer.len());
1766 mode = SelectMode::All;
1767 auto_scroll = false;
1768 }
1769 }
1770
1771 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
1772 if !add {
1773 s.clear_disjoint();
1774 } else if click_count > 1 {
1775 s.delete(newest_selection.id)
1776 }
1777
1778 s.set_pending_anchor_range(start..end, mode);
1779 });
1780 }
1781
1782 fn begin_columnar_selection(
1783 &mut self,
1784 position: DisplayPoint,
1785 goal_column: u32,
1786 cx: &mut ViewContext<Self>,
1787 ) {
1788 if !self.focused {
1789 cx.focus_self();
1790 }
1791
1792 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1793 let tail = self.selections.newest::<Point>(cx).tail();
1794 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1795
1796 self.select_columns(
1797 tail.to_display_point(&display_map),
1798 position,
1799 goal_column,
1800 &display_map,
1801 cx,
1802 );
1803 }
1804
1805 fn update_selection(
1806 &mut self,
1807 position: DisplayPoint,
1808 goal_column: u32,
1809 scroll_position: Vector2F,
1810 cx: &mut ViewContext<Self>,
1811 ) {
1812 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1813
1814 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1815 let tail = tail.to_display_point(&display_map);
1816 self.select_columns(tail, position, goal_column, &display_map, cx);
1817 } else if let Some(mut pending) = self.selections.pending_anchor() {
1818 let buffer = self.buffer.read(cx).snapshot(cx);
1819 let head;
1820 let tail;
1821 let mode = self.selections.pending_mode().unwrap();
1822 match &mode {
1823 SelectMode::Character => {
1824 head = position.to_point(&display_map);
1825 tail = pending.tail().to_point(&buffer);
1826 }
1827 SelectMode::Word(original_range) => {
1828 let original_display_range = original_range.start.to_display_point(&display_map)
1829 ..original_range.end.to_display_point(&display_map);
1830 let original_buffer_range = original_display_range.start.to_point(&display_map)
1831 ..original_display_range.end.to_point(&display_map);
1832 if movement::is_inside_word(&display_map, position)
1833 || original_display_range.contains(&position)
1834 {
1835 let word_range = movement::surrounding_word(&display_map, position);
1836 if word_range.start < original_display_range.start {
1837 head = word_range.start.to_point(&display_map);
1838 } else {
1839 head = word_range.end.to_point(&display_map);
1840 }
1841 } else {
1842 head = position.to_point(&display_map);
1843 }
1844
1845 if head <= original_buffer_range.start {
1846 tail = original_buffer_range.end;
1847 } else {
1848 tail = original_buffer_range.start;
1849 }
1850 }
1851 SelectMode::Line(original_range) => {
1852 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1853
1854 let position = display_map
1855 .clip_point(position, Bias::Left)
1856 .to_point(&display_map);
1857 let line_start = display_map.prev_line_boundary(position).0;
1858 let next_line_start = buffer.clip_point(
1859 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1860 Bias::Left,
1861 );
1862
1863 if line_start < original_range.start {
1864 head = line_start
1865 } else {
1866 head = next_line_start
1867 }
1868
1869 if head <= original_range.start {
1870 tail = original_range.end;
1871 } else {
1872 tail = original_range.start;
1873 }
1874 }
1875 SelectMode::All => {
1876 return;
1877 }
1878 };
1879
1880 if head < tail {
1881 pending.start = buffer.anchor_before(head);
1882 pending.end = buffer.anchor_before(tail);
1883 pending.reversed = true;
1884 } else {
1885 pending.start = buffer.anchor_before(tail);
1886 pending.end = buffer.anchor_before(head);
1887 pending.reversed = false;
1888 }
1889
1890 self.change_selections(None, cx, |s| {
1891 s.set_pending(pending, mode);
1892 });
1893 } else {
1894 error!("update_selection dispatched with no pending selection");
1895 return;
1896 }
1897
1898 self.set_scroll_position(scroll_position, cx);
1899 cx.notify();
1900 }
1901
1902 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1903 self.columnar_selection_tail.take();
1904 if self.selections.pending_anchor().is_some() {
1905 let selections = self.selections.all::<usize>(cx);
1906 self.change_selections(None, cx, |s| {
1907 s.select(selections);
1908 s.clear_pending();
1909 });
1910 }
1911 }
1912
1913 fn select_columns(
1914 &mut self,
1915 tail: DisplayPoint,
1916 head: DisplayPoint,
1917 goal_column: u32,
1918 display_map: &DisplaySnapshot,
1919 cx: &mut ViewContext<Self>,
1920 ) {
1921 let start_row = cmp::min(tail.row(), head.row());
1922 let end_row = cmp::max(tail.row(), head.row());
1923 let start_column = cmp::min(tail.column(), goal_column);
1924 let end_column = cmp::max(tail.column(), goal_column);
1925 let reversed = start_column < tail.column();
1926
1927 let selection_ranges = (start_row..=end_row)
1928 .filter_map(|row| {
1929 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1930 let start = display_map
1931 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1932 .to_point(display_map);
1933 let end = display_map
1934 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1935 .to_point(display_map);
1936 if reversed {
1937 Some(end..start)
1938 } else {
1939 Some(start..end)
1940 }
1941 } else {
1942 None
1943 }
1944 })
1945 .collect::<Vec<_>>();
1946
1947 self.change_selections(None, cx, |s| {
1948 s.select_ranges(selection_ranges);
1949 });
1950 cx.notify();
1951 }
1952
1953 pub fn has_pending_nonempty_selection(&self) -> bool {
1954 let pending_nonempty_selection = match self.selections.pending_anchor() {
1955 Some(Selection { start, end, .. }) => start != end,
1956 None => false,
1957 };
1958 pending_nonempty_selection || self.columnar_selection_tail.is_some()
1959 }
1960
1961 pub fn has_pending_selection(&self) -> bool {
1962 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
1963 }
1964
1965 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1966 if self.take_rename(false, cx).is_some() {
1967 return;
1968 }
1969
1970 if hide_hover(self, cx) {
1971 return;
1972 }
1973
1974 if self.hide_context_menu(cx).is_some() {
1975 return;
1976 }
1977
1978 if self.discard_copilot_suggestion(cx) {
1979 return;
1980 }
1981
1982 if self.snippet_stack.pop().is_some() {
1983 return;
1984 }
1985
1986 if self.mode == EditorMode::Full {
1987 if self.active_diagnostics.is_some() {
1988 self.dismiss_diagnostics(cx);
1989 return;
1990 }
1991
1992 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
1993 return;
1994 }
1995 }
1996
1997 cx.propagate_action();
1998 }
1999
2000 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2001 let text: Arc<str> = text.into();
2002
2003 if self.read_only {
2004 return;
2005 }
2006 if !self.input_enabled {
2007 cx.emit(Event::InputIgnored { text });
2008 return;
2009 }
2010
2011 let selections = self.selections.all_adjusted(cx);
2012 let mut edits = Vec::new();
2013 let mut new_selections = Vec::with_capacity(selections.len());
2014 let mut new_autoclose_regions = Vec::new();
2015 let snapshot = self.buffer.read(cx).read(cx);
2016
2017 for (selection, autoclose_region) in
2018 self.selections_with_autoclose_regions(selections, &snapshot)
2019 {
2020 if let Some(language) = snapshot.language_scope_at(selection.head()) {
2021 // Determine if the inserted text matches the opening or closing
2022 // bracket of any of this language's bracket pairs.
2023 let mut bracket_pair = None;
2024 let mut is_bracket_pair_start = false;
2025 for (pair, enabled) in language.brackets() {
2026 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2027 bracket_pair = Some(pair.clone());
2028 is_bracket_pair_start = true;
2029 break;
2030 } else if pair.end.as_str() == text.as_ref() {
2031 bracket_pair = Some(pair.clone());
2032 break;
2033 }
2034 }
2035
2036 if let Some(bracket_pair) = bracket_pair {
2037 if selection.is_empty() {
2038 if is_bracket_pair_start {
2039 let prefix_len = bracket_pair.start.len() - text.len();
2040
2041 // If the inserted text is a suffix of an opening bracket and the
2042 // selection is preceded by the rest of the opening bracket, then
2043 // insert the closing bracket.
2044 let following_text_allows_autoclose = snapshot
2045 .chars_at(selection.start)
2046 .next()
2047 .map_or(true, |c| language.should_autoclose_before(c));
2048 let preceding_text_matches_prefix = prefix_len == 0
2049 || (selection.start.column >= (prefix_len as u32)
2050 && snapshot.contains_str_at(
2051 Point::new(
2052 selection.start.row,
2053 selection.start.column - (prefix_len as u32),
2054 ),
2055 &bracket_pair.start[..prefix_len],
2056 ));
2057 if following_text_allows_autoclose && preceding_text_matches_prefix {
2058 let anchor = snapshot.anchor_before(selection.end);
2059 new_selections.push((selection.map(|_| anchor), text.len()));
2060 new_autoclose_regions.push((
2061 anchor,
2062 text.len(),
2063 selection.id,
2064 bracket_pair.clone(),
2065 ));
2066 edits.push((
2067 selection.range(),
2068 format!("{}{}", text, bracket_pair.end).into(),
2069 ));
2070 continue;
2071 }
2072 }
2073
2074 if let Some(region) = autoclose_region {
2075 // If the selection is followed by an auto-inserted closing bracket,
2076 // then don't insert that closing bracket again; just move the selection
2077 // past the closing bracket.
2078 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2079 && text.as_ref() == region.pair.end.as_str();
2080 if should_skip {
2081 let anchor = snapshot.anchor_after(selection.end);
2082 new_selections
2083 .push((selection.map(|_| anchor), region.pair.end.len()));
2084 continue;
2085 }
2086 }
2087 }
2088 // If an opening bracket is 1 character long and is typed while
2089 // text is selected, then surround that text with the bracket pair.
2090 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2091 edits.push((selection.start..selection.start, text.clone()));
2092 edits.push((
2093 selection.end..selection.end,
2094 bracket_pair.end.as_str().into(),
2095 ));
2096 new_selections.push((
2097 Selection {
2098 id: selection.id,
2099 start: snapshot.anchor_after(selection.start),
2100 end: snapshot.anchor_before(selection.end),
2101 reversed: selection.reversed,
2102 goal: selection.goal,
2103 },
2104 0,
2105 ));
2106 continue;
2107 }
2108 }
2109 }
2110
2111 // If not handling any auto-close operation, then just replace the selected
2112 // text with the given input and move the selection to the end of the
2113 // newly inserted text.
2114 let anchor = snapshot.anchor_after(selection.end);
2115 new_selections.push((selection.map(|_| anchor), 0));
2116 edits.push((selection.start..selection.end, text.clone()));
2117 }
2118
2119 drop(snapshot);
2120 self.transact(cx, |this, cx| {
2121 this.buffer.update(cx, |buffer, cx| {
2122 buffer.edit(edits, Some(AutoindentMode::EachLine), cx);
2123 });
2124
2125 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2126 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2127 let snapshot = this.buffer.read(cx).read(cx);
2128 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2129 .zip(new_selection_deltas)
2130 .map(|(selection, delta)| selection.map(|e| e + delta))
2131 .collect::<Vec<_>>();
2132
2133 let mut i = 0;
2134 for (position, delta, selection_id, pair) in new_autoclose_regions {
2135 let position = position.to_offset(&snapshot) + delta;
2136 let start = snapshot.anchor_before(position);
2137 let end = snapshot.anchor_after(position);
2138 while let Some(existing_state) = this.autoclose_regions.get(i) {
2139 match existing_state.range.start.cmp(&start, &snapshot) {
2140 Ordering::Less => i += 1,
2141 Ordering::Greater => break,
2142 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2143 Ordering::Less => i += 1,
2144 Ordering::Equal => break,
2145 Ordering::Greater => break,
2146 },
2147 }
2148 }
2149 this.autoclose_regions.insert(
2150 i,
2151 AutocloseRegion {
2152 selection_id,
2153 range: start..end,
2154 pair,
2155 },
2156 );
2157 }
2158
2159 drop(snapshot);
2160 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2161 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2162
2163 // When buffer contents is updated and caret is moved, try triggering on type formatting.
2164 if settings::get::<EditorSettings>(cx).use_on_type_format {
2165 if let Some(on_type_format_task) =
2166 this.trigger_on_type_formatting(text.to_string(), cx)
2167 {
2168 on_type_format_task.detach_and_log_err(cx);
2169 }
2170 }
2171
2172 if had_active_copilot_suggestion {
2173 this.refresh_copilot_suggestions(true, cx);
2174 if !this.has_active_copilot_suggestion(cx) {
2175 this.trigger_completion_on_input(&text, cx);
2176 }
2177 } else {
2178 this.trigger_completion_on_input(&text, cx);
2179 this.refresh_copilot_suggestions(true, cx);
2180 }
2181 });
2182 }
2183
2184 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2185 self.transact(cx, |this, cx| {
2186 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2187 let selections = this.selections.all::<usize>(cx);
2188 let multi_buffer = this.buffer.read(cx);
2189 let buffer = multi_buffer.snapshot(cx);
2190 selections
2191 .iter()
2192 .map(|selection| {
2193 let start_point = selection.start.to_point(&buffer);
2194 let mut indent = buffer.indent_size_for_line(start_point.row);
2195 indent.len = cmp::min(indent.len, start_point.column);
2196 let start = selection.start;
2197 let end = selection.end;
2198 let is_cursor = start == end;
2199 let language_scope = buffer.language_scope_at(start);
2200 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2201 &language_scope
2202 {
2203 let leading_whitespace_len = buffer
2204 .reversed_chars_at(start)
2205 .take_while(|c| c.is_whitespace() && *c != '\n')
2206 .map(|c| c.len_utf8())
2207 .sum::<usize>();
2208
2209 let trailing_whitespace_len = buffer
2210 .chars_at(end)
2211 .take_while(|c| c.is_whitespace() && *c != '\n')
2212 .map(|c| c.len_utf8())
2213 .sum::<usize>();
2214
2215 let insert_extra_newline =
2216 language.brackets().any(|(pair, enabled)| {
2217 let pair_start = pair.start.trim_end();
2218 let pair_end = pair.end.trim_start();
2219
2220 enabled
2221 && pair.newline
2222 && buffer.contains_str_at(
2223 end + trailing_whitespace_len,
2224 pair_end,
2225 )
2226 && buffer.contains_str_at(
2227 (start - leading_whitespace_len)
2228 .saturating_sub(pair_start.len()),
2229 pair_start,
2230 )
2231 });
2232 // Comment extension on newline is allowed only for cursor selections
2233 let comment_delimiter = language.line_comment_prefix().filter(|_| {
2234 let is_comment_extension_enabled =
2235 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2236 is_cursor && is_comment_extension_enabled
2237 });
2238 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2239 buffer
2240 .buffer_line_for_row(start_point.row)
2241 .is_some_and(|(snapshot, range)| {
2242 let mut index_of_first_non_whitespace = 0;
2243 let line_starts_with_comment = snapshot
2244 .chars_for_range(range)
2245 .skip_while(|c| {
2246 let should_skip = c.is_whitespace();
2247 if should_skip {
2248 index_of_first_non_whitespace += 1;
2249 }
2250 should_skip
2251 })
2252 .take(delimiter.len())
2253 .eq(delimiter.chars());
2254 let cursor_is_placed_after_comment_marker =
2255 index_of_first_non_whitespace + delimiter.len()
2256 <= start_point.column as usize;
2257 line_starts_with_comment
2258 && cursor_is_placed_after_comment_marker
2259 })
2260 .then(|| delimiter.clone())
2261 } else {
2262 None
2263 };
2264 (comment_delimiter, insert_extra_newline)
2265 } else {
2266 (None, false)
2267 };
2268
2269 let capacity_for_delimiter = comment_delimiter
2270 .as_deref()
2271 .map(str::len)
2272 .unwrap_or_default();
2273 let mut new_text =
2274 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2275 new_text.push_str("\n");
2276 new_text.extend(indent.chars());
2277 if let Some(delimiter) = &comment_delimiter {
2278 new_text.push_str(&delimiter);
2279 }
2280 if insert_extra_newline {
2281 new_text = new_text.repeat(2);
2282 }
2283
2284 let anchor = buffer.anchor_after(end);
2285 let new_selection = selection.map(|_| anchor);
2286 (
2287 (start..end, new_text),
2288 (insert_extra_newline, new_selection),
2289 )
2290 })
2291 .unzip()
2292 };
2293
2294 this.edit_with_autoindent(edits, cx);
2295 let buffer = this.buffer.read(cx).snapshot(cx);
2296 let new_selections = selection_fixup_info
2297 .into_iter()
2298 .map(|(extra_newline_inserted, new_selection)| {
2299 let mut cursor = new_selection.end.to_point(&buffer);
2300 if extra_newline_inserted {
2301 cursor.row -= 1;
2302 cursor.column = buffer.line_len(cursor.row);
2303 }
2304 new_selection.map(|_| cursor)
2305 })
2306 .collect();
2307
2308 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2309 this.refresh_copilot_suggestions(true, cx);
2310 });
2311 }
2312
2313 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2314 let buffer = self.buffer.read(cx);
2315 let snapshot = buffer.snapshot(cx);
2316
2317 let mut edits = Vec::new();
2318 let mut rows = Vec::new();
2319 let mut rows_inserted = 0;
2320
2321 for selection in self.selections.all_adjusted(cx) {
2322 let cursor = selection.head();
2323 let row = cursor.row;
2324
2325 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2326
2327 let newline = "\n".to_string();
2328 edits.push((start_of_line..start_of_line, newline));
2329
2330 rows.push(row + rows_inserted);
2331 rows_inserted += 1;
2332 }
2333
2334 self.transact(cx, |editor, cx| {
2335 editor.edit(edits, cx);
2336
2337 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2338 let mut index = 0;
2339 s.move_cursors_with(|map, _, _| {
2340 let row = rows[index];
2341 index += 1;
2342
2343 let point = Point::new(row, 0);
2344 let boundary = map.next_line_boundary(point).1;
2345 let clipped = map.clip_point(boundary, Bias::Left);
2346
2347 (clipped, SelectionGoal::None)
2348 });
2349 });
2350
2351 let mut indent_edits = Vec::new();
2352 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2353 for row in rows {
2354 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2355 for (row, indent) in indents {
2356 if indent.len == 0 {
2357 continue;
2358 }
2359
2360 let text = match indent.kind {
2361 IndentKind::Space => " ".repeat(indent.len as usize),
2362 IndentKind::Tab => "\t".repeat(indent.len as usize),
2363 };
2364 let point = Point::new(row, 0);
2365 indent_edits.push((point..point, text));
2366 }
2367 }
2368 editor.edit(indent_edits, cx);
2369 });
2370 }
2371
2372 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2373 let buffer = self.buffer.read(cx);
2374 let snapshot = buffer.snapshot(cx);
2375
2376 let mut edits = Vec::new();
2377 let mut rows = Vec::new();
2378 let mut rows_inserted = 0;
2379
2380 for selection in self.selections.all_adjusted(cx) {
2381 let cursor = selection.head();
2382 let row = cursor.row;
2383
2384 let point = Point::new(row + 1, 0);
2385 let start_of_line = snapshot.clip_point(point, Bias::Left);
2386
2387 let newline = "\n".to_string();
2388 edits.push((start_of_line..start_of_line, newline));
2389
2390 rows_inserted += 1;
2391 rows.push(row + rows_inserted);
2392 }
2393
2394 self.transact(cx, |editor, cx| {
2395 editor.edit(edits, cx);
2396
2397 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2398 let mut index = 0;
2399 s.move_cursors_with(|map, _, _| {
2400 let row = rows[index];
2401 index += 1;
2402
2403 let point = Point::new(row, 0);
2404 let boundary = map.next_line_boundary(point).1;
2405 let clipped = map.clip_point(boundary, Bias::Left);
2406
2407 (clipped, SelectionGoal::None)
2408 });
2409 });
2410
2411 let mut indent_edits = Vec::new();
2412 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2413 for row in rows {
2414 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2415 for (row, indent) in indents {
2416 if indent.len == 0 {
2417 continue;
2418 }
2419
2420 let text = match indent.kind {
2421 IndentKind::Space => " ".repeat(indent.len as usize),
2422 IndentKind::Tab => "\t".repeat(indent.len as usize),
2423 };
2424 let point = Point::new(row, 0);
2425 indent_edits.push((point..point, text));
2426 }
2427 }
2428 editor.edit(indent_edits, cx);
2429 });
2430 }
2431
2432 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2433 self.insert_with_autoindent_mode(
2434 text,
2435 Some(AutoindentMode::Block {
2436 original_indent_columns: Vec::new(),
2437 }),
2438 cx,
2439 );
2440 }
2441
2442 fn insert_with_autoindent_mode(
2443 &mut self,
2444 text: &str,
2445 autoindent_mode: Option<AutoindentMode>,
2446 cx: &mut ViewContext<Self>,
2447 ) {
2448 if self.read_only {
2449 return;
2450 }
2451
2452 let text: Arc<str> = text.into();
2453 self.transact(cx, |this, cx| {
2454 let old_selections = this.selections.all_adjusted(cx);
2455 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2456 let anchors = {
2457 let snapshot = buffer.read(cx);
2458 old_selections
2459 .iter()
2460 .map(|s| {
2461 let anchor = snapshot.anchor_after(s.head());
2462 s.map(|_| anchor)
2463 })
2464 .collect::<Vec<_>>()
2465 };
2466 buffer.edit(
2467 old_selections
2468 .iter()
2469 .map(|s| (s.start..s.end, text.clone())),
2470 autoindent_mode,
2471 cx,
2472 );
2473 anchors
2474 });
2475
2476 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2477 s.select_anchors(selection_anchors);
2478 })
2479 });
2480 }
2481
2482 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2483 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2484 return;
2485 }
2486
2487 let selection = self.selections.newest_anchor();
2488 if self
2489 .buffer
2490 .read(cx)
2491 .is_completion_trigger(selection.head(), text, cx)
2492 {
2493 self.show_completions(&ShowCompletions, cx);
2494 } else {
2495 self.hide_context_menu(cx);
2496 }
2497 }
2498
2499 /// If any empty selections is touching the start of its innermost containing autoclose
2500 /// region, expand it to select the brackets.
2501 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2502 let selections = self.selections.all::<usize>(cx);
2503 let buffer = self.buffer.read(cx).read(cx);
2504 let mut new_selections = Vec::new();
2505 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2506 if let (Some(region), true) = (region, selection.is_empty()) {
2507 let mut range = region.range.to_offset(&buffer);
2508 if selection.start == range.start {
2509 if range.start >= region.pair.start.len() {
2510 range.start -= region.pair.start.len();
2511 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2512 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2513 range.end += region.pair.end.len();
2514 selection.start = range.start;
2515 selection.end = range.end;
2516 }
2517 }
2518 }
2519 }
2520 }
2521 new_selections.push(selection);
2522 }
2523
2524 drop(buffer);
2525 self.change_selections(None, cx, |selections| selections.select(new_selections));
2526 }
2527
2528 /// Iterate the given selections, and for each one, find the smallest surrounding
2529 /// autoclose region. This uses the ordering of the selections and the autoclose
2530 /// regions to avoid repeated comparisons.
2531 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2532 &'a self,
2533 selections: impl IntoIterator<Item = Selection<D>>,
2534 buffer: &'a MultiBufferSnapshot,
2535 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2536 let mut i = 0;
2537 let mut regions = self.autoclose_regions.as_slice();
2538 selections.into_iter().map(move |selection| {
2539 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2540
2541 let mut enclosing = None;
2542 while let Some(pair_state) = regions.get(i) {
2543 if pair_state.range.end.to_offset(buffer) < range.start {
2544 regions = ®ions[i + 1..];
2545 i = 0;
2546 } else if pair_state.range.start.to_offset(buffer) > range.end {
2547 break;
2548 } else if pair_state.selection_id == selection.id {
2549 enclosing = Some(pair_state);
2550 i += 1;
2551 }
2552 }
2553
2554 (selection.clone(), enclosing)
2555 })
2556 }
2557
2558 /// Remove any autoclose regions that no longer contain their selection.
2559 fn invalidate_autoclose_regions(
2560 &mut self,
2561 mut selections: &[Selection<Anchor>],
2562 buffer: &MultiBufferSnapshot,
2563 ) {
2564 self.autoclose_regions.retain(|state| {
2565 let mut i = 0;
2566 while let Some(selection) = selections.get(i) {
2567 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2568 selections = &selections[1..];
2569 continue;
2570 }
2571 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2572 break;
2573 }
2574 if selection.id == state.selection_id {
2575 return true;
2576 } else {
2577 i += 1;
2578 }
2579 }
2580 false
2581 });
2582 }
2583
2584 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2585 let offset = position.to_offset(buffer);
2586 let (word_range, kind) = buffer.surrounding_word(offset);
2587 if offset > word_range.start && kind == Some(CharKind::Word) {
2588 Some(
2589 buffer
2590 .text_for_range(word_range.start..offset)
2591 .collect::<String>(),
2592 )
2593 } else {
2594 None
2595 }
2596 }
2597
2598 fn refresh_inlays(&mut self, reason: InlayRefreshReason, cx: &mut ViewContext<Self>) {
2599 if self.mode != EditorMode::Full {
2600 return;
2601 }
2602
2603 if !settings::get::<EditorSettings>(cx).inlay_hints.enabled {
2604 let to_remove = self.inlay_cache.clear();
2605 self.splice_inlay_hints(to_remove, Vec::new(), cx);
2606 return;
2607 }
2608
2609 match reason {
2610 InlayRefreshReason::Settings(new_settings) => {
2611 let InlaySplice {
2612 to_remove,
2613 to_insert,
2614 } = self.inlay_cache.apply_settings(new_settings);
2615 self.splice_inlay_hints(to_remove, to_insert, cx);
2616 }
2617 InlayRefreshReason::Regular => {
2618 let buffer_handle = self.buffer().clone();
2619 let inlay_fetch_ranges = buffer_handle
2620 .read(cx)
2621 .snapshot(cx)
2622 .excerpts()
2623 .filter_map(|(excerpt_id, buffer_snapshot, excerpt_range)| {
2624 let buffer_path = buffer_snapshot.resolve_file_path(cx, true)?;
2625 let buffer_id = buffer_snapshot.remote_id();
2626 let buffer_version = buffer_snapshot.version().clone();
2627 let max_buffer_offset = buffer_snapshot.len();
2628 let excerpt_range = excerpt_range.context;
2629 Some(QueryInlaysRange {
2630 buffer_path,
2631 buffer_id,
2632 buffer_version,
2633 excerpt_id,
2634 excerpt_offset_range: excerpt_range.start.offset
2635 ..excerpt_range.end.offset.min(max_buffer_offset),
2636 })
2637 })
2638 .collect::<Vec<_>>();
2639
2640 cx.spawn(|editor, mut cx| async move {
2641 let InlaySplice {
2642 to_remove,
2643 to_insert,
2644 } = editor
2645 .update(&mut cx, |editor, cx| {
2646 editor.inlay_cache.fetch_inlays(
2647 buffer_handle,
2648 inlay_fetch_ranges.into_iter(),
2649 cx,
2650 )
2651 })?
2652 .await
2653 .context("inlay cache hint fetch")?;
2654
2655 editor.update(&mut cx, |editor, cx| {
2656 editor.splice_inlay_hints(to_remove, to_insert, cx)
2657 })
2658 })
2659 .detach_and_log_err(cx);
2660 }
2661 }
2662 }
2663
2664 fn splice_inlay_hints(
2665 &self,
2666 to_remove: Vec<InlayId>,
2667 to_insert: Vec<(InlayId, Anchor, project::InlayHint)>,
2668 cx: &mut ViewContext<Self>,
2669 ) {
2670 let buffer = self.buffer.read(cx).read(cx);
2671 let new_inlays: Vec<(InlayId, InlayProperties<String>)> = to_insert
2672 .into_iter()
2673 .map(|(inlay_id, hint_anchor, hint)| {
2674 let mut text = hint.text();
2675 // TODO kb styling instead?
2676 if hint.padding_right {
2677 text.push(' ');
2678 }
2679 if hint.padding_left {
2680 text.insert(0, ' ');
2681 }
2682
2683 (
2684 inlay_id,
2685 InlayProperties {
2686 position: hint_anchor.bias_left(&buffer),
2687 text,
2688 },
2689 )
2690 })
2691 .collect();
2692 drop(buffer);
2693 self.display_map.update(cx, |display_map, cx| {
2694 display_map.splice_inlays(to_remove, new_inlays, cx);
2695 });
2696 }
2697
2698 fn trigger_on_type_formatting(
2699 &self,
2700 input: String,
2701 cx: &mut ViewContext<Self>,
2702 ) -> Option<Task<Result<()>>> {
2703 if input.len() != 1 {
2704 return None;
2705 }
2706
2707 let project = self.project.as_ref()?;
2708 let position = self.selections.newest_anchor().head();
2709 let (buffer, buffer_position) = self
2710 .buffer
2711 .read(cx)
2712 .text_anchor_for_position(position.clone(), cx)?;
2713
2714 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2715 // hence we do LSP request & edit on host side only — add formats to host's history.
2716 let push_to_lsp_host_history = true;
2717 // If this is not the host, append its history with new edits.
2718 let push_to_client_history = project.read(cx).is_remote();
2719
2720 let on_type_formatting = project.update(cx, |project, cx| {
2721 project.on_type_format(
2722 buffer.clone(),
2723 buffer_position,
2724 input,
2725 push_to_lsp_host_history,
2726 cx,
2727 )
2728 });
2729 Some(cx.spawn(|editor, mut cx| async move {
2730 if let Some(transaction) = on_type_formatting.await? {
2731 if push_to_client_history {
2732 buffer.update(&mut cx, |buffer, _| {
2733 buffer.push_transaction(transaction, Instant::now());
2734 });
2735 }
2736 editor.update(&mut cx, |editor, cx| {
2737 editor.refresh_document_highlights(cx);
2738 })?;
2739 }
2740 Ok(())
2741 }))
2742 }
2743
2744 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2745 if self.pending_rename.is_some() {
2746 return;
2747 }
2748
2749 let project = if let Some(project) = self.project.clone() {
2750 project
2751 } else {
2752 return;
2753 };
2754
2755 let position = self.selections.newest_anchor().head();
2756 let (buffer, buffer_position) = if let Some(output) = self
2757 .buffer
2758 .read(cx)
2759 .text_anchor_for_position(position.clone(), cx)
2760 {
2761 output
2762 } else {
2763 return;
2764 };
2765
2766 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2767 let completions = project.update(cx, |project, cx| {
2768 project.completions(&buffer, buffer_position, cx)
2769 });
2770
2771 let id = post_inc(&mut self.next_completion_id);
2772 let task = cx.spawn(|this, mut cx| {
2773 async move {
2774 let menu = if let Some(completions) = completions.await.log_err() {
2775 let mut menu = CompletionsMenu {
2776 id,
2777 initial_position: position,
2778 match_candidates: completions
2779 .iter()
2780 .enumerate()
2781 .map(|(id, completion)| {
2782 StringMatchCandidate::new(
2783 id,
2784 completion.label.text[completion.label.filter_range.clone()]
2785 .into(),
2786 )
2787 })
2788 .collect(),
2789 buffer,
2790 completions: completions.into(),
2791 matches: Vec::new().into(),
2792 selected_item: 0,
2793 list: Default::default(),
2794 };
2795 menu.filter(query.as_deref(), cx.background()).await;
2796 if menu.matches.is_empty() {
2797 None
2798 } else {
2799 Some(menu)
2800 }
2801 } else {
2802 None
2803 };
2804
2805 this.update(&mut cx, |this, cx| {
2806 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2807
2808 match this.context_menu.as_ref() {
2809 None => {}
2810 Some(ContextMenu::Completions(prev_menu)) => {
2811 if prev_menu.id > id {
2812 return;
2813 }
2814 }
2815 _ => return,
2816 }
2817
2818 if this.focused && menu.is_some() {
2819 let menu = menu.unwrap();
2820 this.show_context_menu(ContextMenu::Completions(menu), cx);
2821 } else if this.completion_tasks.is_empty() {
2822 // If there are no more completion tasks and the last menu was
2823 // empty, we should hide it. If it was already hidden, we should
2824 // also show the copilot suggestion when available.
2825 if this.hide_context_menu(cx).is_none() {
2826 this.update_visible_copilot_suggestion(cx);
2827 }
2828 }
2829 })?;
2830
2831 Ok::<_, anyhow::Error>(())
2832 }
2833 .log_err()
2834 });
2835 self.completion_tasks.push((id, task));
2836 }
2837
2838 pub fn confirm_completion(
2839 &mut self,
2840 action: &ConfirmCompletion,
2841 cx: &mut ViewContext<Self>,
2842 ) -> Option<Task<Result<()>>> {
2843 use language::ToOffset as _;
2844
2845 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2846 menu
2847 } else {
2848 return None;
2849 };
2850
2851 let mat = completions_menu
2852 .matches
2853 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2854 let buffer_handle = completions_menu.buffer;
2855 let completion = completions_menu.completions.get(mat.candidate_id)?;
2856
2857 let snippet;
2858 let text;
2859 if completion.is_snippet() {
2860 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2861 text = snippet.as_ref().unwrap().text.clone();
2862 } else {
2863 snippet = None;
2864 text = completion.new_text.clone();
2865 };
2866 let selections = self.selections.all::<usize>(cx);
2867 let buffer = buffer_handle.read(cx);
2868 let old_range = completion.old_range.to_offset(buffer);
2869 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2870
2871 let newest_selection = self.selections.newest_anchor();
2872 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2873 return None;
2874 }
2875
2876 let lookbehind = newest_selection
2877 .start
2878 .text_anchor
2879 .to_offset(buffer)
2880 .saturating_sub(old_range.start);
2881 let lookahead = old_range
2882 .end
2883 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2884 let mut common_prefix_len = old_text
2885 .bytes()
2886 .zip(text.bytes())
2887 .take_while(|(a, b)| a == b)
2888 .count();
2889
2890 let snapshot = self.buffer.read(cx).snapshot(cx);
2891 let mut ranges = Vec::new();
2892 for selection in &selections {
2893 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2894 let start = selection.start.saturating_sub(lookbehind);
2895 let end = selection.end + lookahead;
2896 ranges.push(start + common_prefix_len..end);
2897 } else {
2898 common_prefix_len = 0;
2899 ranges.clear();
2900 ranges.extend(selections.iter().map(|s| {
2901 if s.id == newest_selection.id {
2902 old_range.clone()
2903 } else {
2904 s.start..s.end
2905 }
2906 }));
2907 break;
2908 }
2909 }
2910 let text = &text[common_prefix_len..];
2911
2912 self.transact(cx, |this, cx| {
2913 if let Some(mut snippet) = snippet {
2914 snippet.text = text.to_string();
2915 for tabstop in snippet.tabstops.iter_mut().flatten() {
2916 tabstop.start -= common_prefix_len as isize;
2917 tabstop.end -= common_prefix_len as isize;
2918 }
2919
2920 this.insert_snippet(&ranges, snippet, cx).log_err();
2921 } else {
2922 this.buffer.update(cx, |buffer, cx| {
2923 buffer.edit(
2924 ranges.iter().map(|range| (range.clone(), text)),
2925 Some(AutoindentMode::EachLine),
2926 cx,
2927 );
2928 });
2929 }
2930
2931 this.refresh_copilot_suggestions(true, cx);
2932 });
2933
2934 let project = self.project.clone()?;
2935 let apply_edits = project.update(cx, |project, cx| {
2936 project.apply_additional_edits_for_completion(
2937 buffer_handle,
2938 completion.clone(),
2939 true,
2940 cx,
2941 )
2942 });
2943 Some(cx.foreground().spawn(async move {
2944 apply_edits.await?;
2945 Ok(())
2946 }))
2947 }
2948
2949 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2950 if matches!(
2951 self.context_menu.as_ref(),
2952 Some(ContextMenu::CodeActions(_))
2953 ) {
2954 self.context_menu.take();
2955 cx.notify();
2956 return;
2957 }
2958
2959 let deployed_from_indicator = action.deployed_from_indicator;
2960 let mut task = self.code_actions_task.take();
2961 cx.spawn(|this, mut cx| async move {
2962 while let Some(prev_task) = task {
2963 prev_task.await;
2964 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
2965 }
2966
2967 this.update(&mut cx, |this, cx| {
2968 if this.focused {
2969 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2970 this.show_context_menu(
2971 ContextMenu::CodeActions(CodeActionsMenu {
2972 buffer,
2973 actions,
2974 selected_item: Default::default(),
2975 list: Default::default(),
2976 deployed_from_indicator,
2977 }),
2978 cx,
2979 );
2980 }
2981 }
2982 })?;
2983
2984 Ok::<_, anyhow::Error>(())
2985 })
2986 .detach_and_log_err(cx);
2987 }
2988
2989 pub fn confirm_code_action(
2990 workspace: &mut Workspace,
2991 action: &ConfirmCodeAction,
2992 cx: &mut ViewContext<Workspace>,
2993 ) -> Option<Task<Result<()>>> {
2994 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2995 let actions_menu = if let ContextMenu::CodeActions(menu) =
2996 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2997 {
2998 menu
2999 } else {
3000 return None;
3001 };
3002 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3003 let action = actions_menu.actions.get(action_ix)?.clone();
3004 let title = action.lsp_action.title.clone();
3005 let buffer = actions_menu.buffer;
3006
3007 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3008 project.apply_code_action(buffer, action, true, cx)
3009 });
3010 let editor = editor.downgrade();
3011 Some(cx.spawn(|workspace, cx| async move {
3012 let project_transaction = apply_code_actions.await?;
3013 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3014 }))
3015 }
3016
3017 async fn open_project_transaction(
3018 this: &WeakViewHandle<Editor>,
3019 workspace: WeakViewHandle<Workspace>,
3020 transaction: ProjectTransaction,
3021 title: String,
3022 mut cx: AsyncAppContext,
3023 ) -> Result<()> {
3024 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3025
3026 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3027 entries.sort_unstable_by_key(|(buffer, _)| {
3028 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3029 });
3030
3031 // If the project transaction's edits are all contained within this editor, then
3032 // avoid opening a new editor to display them.
3033
3034 if let Some((buffer, transaction)) = entries.first() {
3035 if entries.len() == 1 {
3036 let excerpt = this.read_with(&cx, |editor, cx| {
3037 editor
3038 .buffer()
3039 .read(cx)
3040 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3041 })?;
3042 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3043 if excerpted_buffer == *buffer {
3044 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3045 let excerpt_range = excerpt_range.to_offset(buffer);
3046 buffer
3047 .edited_ranges_for_transaction::<usize>(transaction)
3048 .all(|range| {
3049 excerpt_range.start <= range.start
3050 && excerpt_range.end >= range.end
3051 })
3052 });
3053
3054 if all_edits_within_excerpt {
3055 return Ok(());
3056 }
3057 }
3058 }
3059 }
3060 } else {
3061 return Ok(());
3062 }
3063
3064 let mut ranges_to_highlight = Vec::new();
3065 let excerpt_buffer = cx.add_model(|cx| {
3066 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3067 for (buffer_handle, transaction) in &entries {
3068 let buffer = buffer_handle.read(cx);
3069 ranges_to_highlight.extend(
3070 multibuffer.push_excerpts_with_context_lines(
3071 buffer_handle.clone(),
3072 buffer
3073 .edited_ranges_for_transaction::<usize>(transaction)
3074 .collect(),
3075 1,
3076 cx,
3077 ),
3078 );
3079 }
3080 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3081 multibuffer
3082 });
3083
3084 workspace.update(&mut cx, |workspace, cx| {
3085 let project = workspace.project().clone();
3086 let editor =
3087 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3088 workspace.add_item(Box::new(editor.clone()), cx);
3089 editor.update(cx, |editor, cx| {
3090 editor.highlight_background::<Self>(
3091 ranges_to_highlight,
3092 |theme| theme.editor.highlighted_line_background,
3093 cx,
3094 );
3095 });
3096 })?;
3097
3098 Ok(())
3099 }
3100
3101 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3102 let project = self.project.as_ref()?;
3103 let buffer = self.buffer.read(cx);
3104 let newest_selection = self.selections.newest_anchor().clone();
3105 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3106 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3107 if start_buffer != end_buffer {
3108 return None;
3109 }
3110
3111 let actions = project.update(cx, |project, cx| {
3112 project.code_actions(&start_buffer, start..end, cx)
3113 });
3114 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3115 let actions = actions.await;
3116 this.update(&mut cx, |this, cx| {
3117 this.available_code_actions = actions.log_err().and_then(|actions| {
3118 if actions.is_empty() {
3119 None
3120 } else {
3121 Some((start_buffer, actions.into()))
3122 }
3123 });
3124 cx.notify();
3125 })
3126 .log_err();
3127 }));
3128 None
3129 }
3130
3131 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3132 if self.pending_rename.is_some() {
3133 return None;
3134 }
3135
3136 let project = self.project.as_ref()?;
3137 let buffer = self.buffer.read(cx);
3138 let newest_selection = self.selections.newest_anchor().clone();
3139 let cursor_position = newest_selection.head();
3140 let (cursor_buffer, cursor_buffer_position) =
3141 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3142 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3143 if cursor_buffer != tail_buffer {
3144 return None;
3145 }
3146
3147 let highlights = project.update(cx, |project, cx| {
3148 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3149 });
3150
3151 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3152 if let Some(highlights) = highlights.await.log_err() {
3153 this.update(&mut cx, |this, cx| {
3154 if this.pending_rename.is_some() {
3155 return;
3156 }
3157
3158 let buffer_id = cursor_position.buffer_id;
3159 let buffer = this.buffer.read(cx);
3160 if !buffer
3161 .text_anchor_for_position(cursor_position, cx)
3162 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3163 {
3164 return;
3165 }
3166
3167 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3168 let mut write_ranges = Vec::new();
3169 let mut read_ranges = Vec::new();
3170 for highlight in highlights {
3171 for (excerpt_id, excerpt_range) in
3172 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3173 {
3174 let start = highlight
3175 .range
3176 .start
3177 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3178 let end = highlight
3179 .range
3180 .end
3181 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3182 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3183 continue;
3184 }
3185
3186 let range = Anchor {
3187 buffer_id,
3188 excerpt_id: excerpt_id.clone(),
3189 text_anchor: start,
3190 }..Anchor {
3191 buffer_id,
3192 excerpt_id,
3193 text_anchor: end,
3194 };
3195 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3196 write_ranges.push(range);
3197 } else {
3198 read_ranges.push(range);
3199 }
3200 }
3201 }
3202
3203 this.highlight_background::<DocumentHighlightRead>(
3204 read_ranges,
3205 |theme| theme.editor.document_highlight_read_background,
3206 cx,
3207 );
3208 this.highlight_background::<DocumentHighlightWrite>(
3209 write_ranges,
3210 |theme| theme.editor.document_highlight_write_background,
3211 cx,
3212 );
3213 cx.notify();
3214 })
3215 .log_err();
3216 }
3217 }));
3218 None
3219 }
3220
3221 fn refresh_copilot_suggestions(
3222 &mut self,
3223 debounce: bool,
3224 cx: &mut ViewContext<Self>,
3225 ) -> Option<()> {
3226 let copilot = Copilot::global(cx)?;
3227 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3228 self.clear_copilot_suggestions(cx);
3229 return None;
3230 }
3231 self.update_visible_copilot_suggestion(cx);
3232
3233 let snapshot = self.buffer.read(cx).snapshot(cx);
3234 let cursor = self.selections.newest_anchor().head();
3235 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3236 self.clear_copilot_suggestions(cx);
3237 return None;
3238 }
3239
3240 let (buffer, buffer_position) =
3241 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3242 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3243 if debounce {
3244 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3245 }
3246
3247 let completions = copilot
3248 .update(&mut cx, |copilot, cx| {
3249 copilot.completions(&buffer, buffer_position, cx)
3250 })
3251 .await
3252 .log_err()
3253 .into_iter()
3254 .flatten()
3255 .collect_vec();
3256
3257 this.update(&mut cx, |this, cx| {
3258 if !completions.is_empty() {
3259 this.copilot_state.cycled = false;
3260 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3261 this.copilot_state.completions.clear();
3262 this.copilot_state.active_completion_index = 0;
3263 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3264 for completion in completions {
3265 this.copilot_state.push_completion(completion);
3266 }
3267 this.update_visible_copilot_suggestion(cx);
3268 }
3269 })
3270 .log_err()?;
3271 Some(())
3272 });
3273
3274 Some(())
3275 }
3276
3277 fn cycle_copilot_suggestions(
3278 &mut self,
3279 direction: Direction,
3280 cx: &mut ViewContext<Self>,
3281 ) -> Option<()> {
3282 let copilot = Copilot::global(cx)?;
3283 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3284 return None;
3285 }
3286
3287 if self.copilot_state.cycled {
3288 self.copilot_state.cycle_completions(direction);
3289 self.update_visible_copilot_suggestion(cx);
3290 } else {
3291 let cursor = self.selections.newest_anchor().head();
3292 let (buffer, buffer_position) =
3293 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3294 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3295 let completions = copilot
3296 .update(&mut cx, |copilot, cx| {
3297 copilot.completions_cycling(&buffer, buffer_position, cx)
3298 })
3299 .await;
3300
3301 this.update(&mut cx, |this, cx| {
3302 this.copilot_state.cycled = true;
3303 for completion in completions.log_err().into_iter().flatten() {
3304 this.copilot_state.push_completion(completion);
3305 }
3306 this.copilot_state.cycle_completions(direction);
3307 this.update_visible_copilot_suggestion(cx);
3308 })
3309 .log_err()?;
3310
3311 Some(())
3312 });
3313 }
3314
3315 Some(())
3316 }
3317
3318 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3319 if !self.has_active_copilot_suggestion(cx) {
3320 self.refresh_copilot_suggestions(false, cx);
3321 return;
3322 }
3323
3324 self.update_visible_copilot_suggestion(cx);
3325 }
3326
3327 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3328 if self.has_active_copilot_suggestion(cx) {
3329 self.cycle_copilot_suggestions(Direction::Next, cx);
3330 } else {
3331 self.refresh_copilot_suggestions(false, cx);
3332 }
3333 }
3334
3335 fn previous_copilot_suggestion(
3336 &mut self,
3337 _: &copilot::PreviousSuggestion,
3338 cx: &mut ViewContext<Self>,
3339 ) {
3340 if self.has_active_copilot_suggestion(cx) {
3341 self.cycle_copilot_suggestions(Direction::Prev, cx);
3342 } else {
3343 self.refresh_copilot_suggestions(false, cx);
3344 }
3345 }
3346
3347 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3348 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3349 if let Some((copilot, completion)) =
3350 Copilot::global(cx).zip(self.copilot_state.active_completion())
3351 {
3352 copilot
3353 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3354 .detach_and_log_err(cx);
3355
3356 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3357 }
3358 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3359 cx.notify();
3360 true
3361 } else {
3362 false
3363 }
3364 }
3365
3366 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3367 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3368 if let Some(copilot) = Copilot::global(cx) {
3369 copilot
3370 .update(cx, |copilot, cx| {
3371 copilot.discard_completions(&self.copilot_state.completions, cx)
3372 })
3373 .detach_and_log_err(cx);
3374
3375 self.report_copilot_event(None, false, cx)
3376 }
3377
3378 self.display_map.update(cx, |map, cx| {
3379 map.splice_inlays::<&str>(vec![suggestion.id], Vec::new(), cx)
3380 });
3381 cx.notify();
3382 true
3383 } else {
3384 false
3385 }
3386 }
3387
3388 fn is_copilot_enabled_at(
3389 &self,
3390 location: Anchor,
3391 snapshot: &MultiBufferSnapshot,
3392 cx: &mut ViewContext<Self>,
3393 ) -> bool {
3394 let file = snapshot.file_at(location);
3395 let language = snapshot.language_at(location);
3396 let settings = all_language_settings(file, cx);
3397 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3398 }
3399
3400 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3401 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3402 let buffer = self.buffer.read(cx).read(cx);
3403 suggestion.position.is_valid(&buffer)
3404 } else {
3405 false
3406 }
3407 }
3408
3409 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3410 let suggestion = self.copilot_state.suggestion.take()?;
3411 self.display_map.update(cx, |map, cx| {
3412 map.splice_inlays::<&str>(vec![suggestion.id], Default::default(), cx);
3413 });
3414 let buffer = self.buffer.read(cx).read(cx);
3415
3416 if suggestion.position.is_valid(&buffer) {
3417 Some(suggestion)
3418 } else {
3419 None
3420 }
3421 }
3422
3423 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3424 let snapshot = self.buffer.read(cx).snapshot(cx);
3425 let selection = self.selections.newest_anchor();
3426 let cursor = selection.head();
3427
3428 if self.context_menu.is_some()
3429 || !self.completion_tasks.is_empty()
3430 || selection.start != selection.end
3431 {
3432 self.discard_copilot_suggestion(cx);
3433 } else if let Some(text) = self
3434 .copilot_state
3435 .text_for_active_completion(cursor, &snapshot)
3436 {
3437 let text = Rope::from(text);
3438 let mut to_remove = Vec::new();
3439 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3440 to_remove.push(suggestion.id);
3441 }
3442
3443 let to_insert = vec![(
3444 // TODO kb check how can I get the unique id for the suggestion
3445 // Move the generation of the id inside the map
3446 InlayId(usize::MAX),
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: InlayId(usize::MAX),
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_inlay_hints = 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 true
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 true
7289 }
7290 multi_buffer::Event::Reloaded => {
7291 cx.emit(Event::TitleChanged);
7292 true
7293 }
7294 multi_buffer::Event::DiffBaseChanged => {
7295 cx.emit(Event::DiffBaseChanged);
7296 true
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_inlay_hints {
7310 self.refresh_inlays(InlayRefreshReason::Regular, 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::Settings(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
7615fn consume_contiguous_rows(
7616 contiguous_row_selections: &mut Vec<Selection<Point>>,
7617 selection: &Selection<Point>,
7618 display_map: &DisplaySnapshot,
7619 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7620) -> (u32, u32) {
7621 contiguous_row_selections.push(selection.clone());
7622 let start_row = selection.start.row;
7623 let mut end_row = ending_row(selection, display_map);
7624
7625 while let Some(next_selection) = selections.peek() {
7626 if next_selection.start.row <= end_row {
7627 end_row = ending_row(next_selection, display_map);
7628 contiguous_row_selections.push(selections.next().unwrap().clone());
7629 } else {
7630 break;
7631 }
7632 }
7633 (start_row, end_row)
7634}
7635
7636fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7637 if next_selection.end.column > 0 || next_selection.is_empty() {
7638 display_map.next_line_boundary(next_selection.end).0.row + 1
7639 } else {
7640 next_selection.end.row
7641 }
7642}
7643
7644impl EditorSnapshot {
7645 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7646 self.display_snapshot.buffer_snapshot.language_at(position)
7647 }
7648
7649 pub fn is_focused(&self) -> bool {
7650 self.is_focused
7651 }
7652
7653 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7654 self.placeholder_text.as_ref()
7655 }
7656
7657 pub fn scroll_position(&self) -> Vector2F {
7658 self.scroll_anchor.scroll_position(&self.display_snapshot)
7659 }
7660}
7661
7662impl Deref for EditorSnapshot {
7663 type Target = DisplaySnapshot;
7664
7665 fn deref(&self) -> &Self::Target {
7666 &self.display_snapshot
7667 }
7668}
7669
7670#[derive(Clone, Debug, PartialEq, Eq)]
7671pub enum Event {
7672 InputIgnored {
7673 text: Arc<str>,
7674 },
7675 ExcerptsAdded {
7676 buffer: ModelHandle<Buffer>,
7677 predecessor: ExcerptId,
7678 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7679 },
7680 ExcerptsRemoved {
7681 ids: Vec<ExcerptId>,
7682 },
7683 BufferEdited,
7684 Edited,
7685 Reparsed,
7686 Focused,
7687 Blurred,
7688 DirtyChanged,
7689 Saved,
7690 TitleChanged,
7691 DiffBaseChanged,
7692 SelectionsChanged {
7693 local: bool,
7694 },
7695 ScrollPositionChanged {
7696 local: bool,
7697 autoscroll: bool,
7698 },
7699 Closed,
7700}
7701
7702pub struct EditorFocused(pub ViewHandle<Editor>);
7703pub struct EditorBlurred(pub ViewHandle<Editor>);
7704pub struct EditorReleased(pub WeakViewHandle<Editor>);
7705
7706impl Entity for Editor {
7707 type Event = Event;
7708
7709 fn release(&mut self, cx: &mut AppContext) {
7710 cx.emit_global(EditorReleased(self.handle.clone()));
7711 }
7712}
7713
7714impl View for Editor {
7715 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7716 let style = self.style(cx);
7717 let font_changed = self.display_map.update(cx, |map, cx| {
7718 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7719 map.set_font(style.text.font_id, style.text.font_size, cx)
7720 });
7721
7722 if font_changed {
7723 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7724 hide_hover(editor, cx);
7725 hide_link_definition(editor, cx);
7726 });
7727 }
7728
7729 Stack::new()
7730 .with_child(EditorElement::new(style.clone()))
7731 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7732 .into_any()
7733 }
7734
7735 fn ui_name() -> &'static str {
7736 "Editor"
7737 }
7738
7739 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7740 if cx.is_self_focused() {
7741 let focused_event = EditorFocused(cx.handle());
7742 cx.emit(Event::Focused);
7743 cx.emit_global(focused_event);
7744 }
7745 if let Some(rename) = self.pending_rename.as_ref() {
7746 cx.focus(&rename.editor);
7747 } else {
7748 if !self.focused {
7749 self.blink_manager.update(cx, BlinkManager::enable);
7750 }
7751 self.focused = true;
7752 self.buffer.update(cx, |buffer, cx| {
7753 buffer.finalize_last_transaction(cx);
7754 if self.leader_replica_id.is_none() {
7755 buffer.set_active_selections(
7756 &self.selections.disjoint_anchors(),
7757 self.selections.line_mode,
7758 self.cursor_shape,
7759 cx,
7760 );
7761 }
7762 });
7763 }
7764 }
7765
7766 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7767 let blurred_event = EditorBlurred(cx.handle());
7768 cx.emit_global(blurred_event);
7769 self.focused = false;
7770 self.blink_manager.update(cx, BlinkManager::disable);
7771 self.buffer
7772 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7773 self.hide_context_menu(cx);
7774 hide_hover(self, cx);
7775 cx.emit(Event::Blurred);
7776 cx.notify();
7777 }
7778
7779 fn modifiers_changed(
7780 &mut self,
7781 event: &gpui::platform::ModifiersChangedEvent,
7782 cx: &mut ViewContext<Self>,
7783 ) -> bool {
7784 let pending_selection = self.has_pending_selection();
7785
7786 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7787 if event.cmd && !pending_selection {
7788 let snapshot = self.snapshot(cx);
7789 let kind = if event.shift {
7790 LinkDefinitionKind::Type
7791 } else {
7792 LinkDefinitionKind::Symbol
7793 };
7794
7795 show_link_definition(kind, self, point, snapshot, cx);
7796 return false;
7797 }
7798 }
7799
7800 {
7801 if self.link_go_to_definition_state.symbol_range.is_some()
7802 || !self.link_go_to_definition_state.definitions.is_empty()
7803 {
7804 self.link_go_to_definition_state.symbol_range.take();
7805 self.link_go_to_definition_state.definitions.clear();
7806 cx.notify();
7807 }
7808
7809 self.link_go_to_definition_state.task = None;
7810
7811 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7812 }
7813
7814 false
7815 }
7816
7817 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7818 Self::reset_to_default_keymap_context(keymap);
7819 let mode = match self.mode {
7820 EditorMode::SingleLine => "single_line",
7821 EditorMode::AutoHeight { .. } => "auto_height",
7822 EditorMode::Full => "full",
7823 };
7824 keymap.add_key("mode", mode);
7825 if self.pending_rename.is_some() {
7826 keymap.add_identifier("renaming");
7827 }
7828 match self.context_menu.as_ref() {
7829 Some(ContextMenu::Completions(_)) => {
7830 keymap.add_identifier("menu");
7831 keymap.add_identifier("showing_completions")
7832 }
7833 Some(ContextMenu::CodeActions(_)) => {
7834 keymap.add_identifier("menu");
7835 keymap.add_identifier("showing_code_actions")
7836 }
7837 None => {}
7838 }
7839 for layer in self.keymap_context_layers.values() {
7840 keymap.extend(layer);
7841 }
7842
7843 if let Some(extension) = self
7844 .buffer
7845 .read(cx)
7846 .as_singleton()
7847 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7848 {
7849 keymap.add_key("extension", extension.to_string());
7850 }
7851 }
7852
7853 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7854 Some(
7855 self.buffer
7856 .read(cx)
7857 .read(cx)
7858 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7859 .collect(),
7860 )
7861 }
7862
7863 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7864 // Prevent the IME menu from appearing when holding down an alphabetic key
7865 // while input is disabled.
7866 if !self.input_enabled {
7867 return None;
7868 }
7869
7870 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7871 Some(range.start.0..range.end.0)
7872 }
7873
7874 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7875 let snapshot = self.buffer.read(cx).read(cx);
7876 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7877 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7878 }
7879
7880 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7881 self.clear_text_highlights::<InputComposition>(cx);
7882 self.ime_transaction.take();
7883 }
7884
7885 fn replace_text_in_range(
7886 &mut self,
7887 range_utf16: Option<Range<usize>>,
7888 text: &str,
7889 cx: &mut ViewContext<Self>,
7890 ) {
7891 self.transact(cx, |this, cx| {
7892 if this.input_enabled {
7893 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7894 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7895 Some(this.selection_replacement_ranges(range_utf16, cx))
7896 } else {
7897 this.marked_text_ranges(cx)
7898 };
7899
7900 if let Some(new_selected_ranges) = new_selected_ranges {
7901 this.change_selections(None, cx, |selections| {
7902 selections.select_ranges(new_selected_ranges)
7903 });
7904 }
7905 }
7906
7907 this.handle_input(text, cx);
7908 });
7909
7910 if !self.input_enabled {
7911 return;
7912 }
7913
7914 if let Some(transaction) = self.ime_transaction {
7915 self.buffer.update(cx, |buffer, cx| {
7916 buffer.group_until_transaction(transaction, cx);
7917 });
7918 }
7919
7920 self.unmark_text(cx);
7921 }
7922
7923 fn replace_and_mark_text_in_range(
7924 &mut self,
7925 range_utf16: Option<Range<usize>>,
7926 text: &str,
7927 new_selected_range_utf16: Option<Range<usize>>,
7928 cx: &mut ViewContext<Self>,
7929 ) {
7930 if !self.input_enabled {
7931 return;
7932 }
7933
7934 let transaction = self.transact(cx, |this, cx| {
7935 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7936 let snapshot = this.buffer.read(cx).read(cx);
7937 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7938 for marked_range in &mut marked_ranges {
7939 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7940 marked_range.start.0 += relative_range_utf16.start;
7941 marked_range.start =
7942 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7943 marked_range.end =
7944 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7945 }
7946 }
7947 Some(marked_ranges)
7948 } else if let Some(range_utf16) = range_utf16 {
7949 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7950 Some(this.selection_replacement_ranges(range_utf16, cx))
7951 } else {
7952 None
7953 };
7954
7955 if let Some(ranges) = ranges_to_replace {
7956 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7957 }
7958
7959 let marked_ranges = {
7960 let snapshot = this.buffer.read(cx).read(cx);
7961 this.selections
7962 .disjoint_anchors()
7963 .iter()
7964 .map(|selection| {
7965 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
7966 })
7967 .collect::<Vec<_>>()
7968 };
7969
7970 if text.is_empty() {
7971 this.unmark_text(cx);
7972 } else {
7973 this.highlight_text::<InputComposition>(
7974 marked_ranges.clone(),
7975 this.style(cx).composition_mark,
7976 cx,
7977 );
7978 }
7979
7980 this.handle_input(text, cx);
7981
7982 if let Some(new_selected_range) = new_selected_range_utf16 {
7983 let snapshot = this.buffer.read(cx).read(cx);
7984 let new_selected_ranges = marked_ranges
7985 .into_iter()
7986 .map(|marked_range| {
7987 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
7988 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
7989 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
7990 snapshot.clip_offset_utf16(new_start, Bias::Left)
7991 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
7992 })
7993 .collect::<Vec<_>>();
7994
7995 drop(snapshot);
7996 this.change_selections(None, cx, |selections| {
7997 selections.select_ranges(new_selected_ranges)
7998 });
7999 }
8000 });
8001
8002 self.ime_transaction = self.ime_transaction.or(transaction);
8003 if let Some(transaction) = self.ime_transaction {
8004 self.buffer.update(cx, |buffer, cx| {
8005 buffer.group_until_transaction(transaction, cx);
8006 });
8007 }
8008
8009 if self.text_highlights::<InputComposition>(cx).is_none() {
8010 self.ime_transaction.take();
8011 }
8012 }
8013}
8014
8015fn build_style(
8016 settings: &ThemeSettings,
8017 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8018 override_text_style: Option<&OverrideTextStyle>,
8019 cx: &AppContext,
8020) -> EditorStyle {
8021 let font_cache = cx.font_cache();
8022
8023 let theme_id = settings.theme.meta.id;
8024 let mut theme = settings.theme.editor.clone();
8025 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8026 let field_editor_theme = get_field_editor_theme(&settings.theme);
8027 theme.text_color = field_editor_theme.text.color;
8028 theme.selection = field_editor_theme.selection;
8029 theme.background = field_editor_theme
8030 .container
8031 .background_color
8032 .unwrap_or_default();
8033 EditorStyle {
8034 text: field_editor_theme.text,
8035 placeholder_text: field_editor_theme.placeholder_text,
8036 theme,
8037 theme_id,
8038 }
8039 } else {
8040 let font_family_id = settings.buffer_font_family;
8041 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8042 let font_properties = Default::default();
8043 let font_id = font_cache
8044 .select_font(font_family_id, &font_properties)
8045 .unwrap();
8046 let font_size = settings.buffer_font_size(cx);
8047 EditorStyle {
8048 text: TextStyle {
8049 color: settings.theme.editor.text_color,
8050 font_family_name,
8051 font_family_id,
8052 font_id,
8053 font_size,
8054 font_properties,
8055 underline: Default::default(),
8056 },
8057 placeholder_text: None,
8058 theme,
8059 theme_id,
8060 }
8061 };
8062
8063 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8064 if let Some(highlighted) = style
8065 .text
8066 .clone()
8067 .highlight(highlight_style, font_cache)
8068 .log_err()
8069 {
8070 style.text = highlighted;
8071 }
8072 }
8073
8074 style
8075}
8076
8077trait SelectionExt {
8078 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8079 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8080 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8081 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8082 -> Range<u32>;
8083}
8084
8085impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8086 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8087 let start = self.start.to_point(buffer);
8088 let end = self.end.to_point(buffer);
8089 if self.reversed {
8090 end..start
8091 } else {
8092 start..end
8093 }
8094 }
8095
8096 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8097 let start = self.start.to_offset(buffer);
8098 let end = self.end.to_offset(buffer);
8099 if self.reversed {
8100 end..start
8101 } else {
8102 start..end
8103 }
8104 }
8105
8106 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8107 let start = self
8108 .start
8109 .to_point(&map.buffer_snapshot)
8110 .to_display_point(map);
8111 let end = self
8112 .end
8113 .to_point(&map.buffer_snapshot)
8114 .to_display_point(map);
8115 if self.reversed {
8116 end..start
8117 } else {
8118 start..end
8119 }
8120 }
8121
8122 fn spanned_rows(
8123 &self,
8124 include_end_if_at_line_start: bool,
8125 map: &DisplaySnapshot,
8126 ) -> Range<u32> {
8127 let start = self.start.to_point(&map.buffer_snapshot);
8128 let mut end = self.end.to_point(&map.buffer_snapshot);
8129 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8130 end.row -= 1;
8131 }
8132
8133 let buffer_start = map.prev_line_boundary(start).0;
8134 let buffer_end = map.next_line_boundary(end).0;
8135 buffer_start.row..buffer_end.row + 1
8136 }
8137}
8138
8139impl<T: InvalidationRegion> InvalidationStack<T> {
8140 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8141 where
8142 S: Clone + ToOffset,
8143 {
8144 while let Some(region) = self.last() {
8145 let all_selections_inside_invalidation_ranges =
8146 if selections.len() == region.ranges().len() {
8147 selections
8148 .iter()
8149 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8150 .all(|(selection, invalidation_range)| {
8151 let head = selection.head().to_offset(buffer);
8152 invalidation_range.start <= head && invalidation_range.end >= head
8153 })
8154 } else {
8155 false
8156 };
8157
8158 if all_selections_inside_invalidation_ranges {
8159 break;
8160 } else {
8161 self.pop();
8162 }
8163 }
8164 }
8165}
8166
8167impl<T> Default for InvalidationStack<T> {
8168 fn default() -> Self {
8169 Self(Default::default())
8170 }
8171}
8172
8173impl<T> Deref for InvalidationStack<T> {
8174 type Target = Vec<T>;
8175
8176 fn deref(&self) -> &Self::Target {
8177 &self.0
8178 }
8179}
8180
8181impl<T> DerefMut for InvalidationStack<T> {
8182 fn deref_mut(&mut self) -> &mut Self::Target {
8183 &mut self.0
8184 }
8185}
8186
8187impl InvalidationRegion for SnippetState {
8188 fn ranges(&self) -> &[Range<Anchor>] {
8189 &self.ranges[self.active_index]
8190 }
8191}
8192
8193impl Deref for EditorStyle {
8194 type Target = theme::Editor;
8195
8196 fn deref(&self) -> &Self::Target {
8197 &self.theme
8198 }
8199}
8200
8201pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8202 let mut highlighted_lines = Vec::new();
8203
8204 for (index, line) in diagnostic.message.lines().enumerate() {
8205 let line = match &diagnostic.source {
8206 Some(source) if index == 0 => {
8207 let source_highlight = Vec::from_iter(0..source.len());
8208 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8209 }
8210
8211 _ => highlight_diagnostic_message(Vec::new(), line),
8212 };
8213 highlighted_lines.push(line);
8214 }
8215 let message = diagnostic.message;
8216 Arc::new(move |cx: &mut BlockContext| {
8217 let message = message.clone();
8218 let settings = settings::get::<ThemeSettings>(cx);
8219 let tooltip_style = settings.theme.tooltip.clone();
8220 let theme = &settings.theme.editor;
8221 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8222 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8223 let anchor_x = cx.anchor_x;
8224 enum BlockContextToolip {}
8225 MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
8226 Flex::column()
8227 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8228 Label::new(
8229 line.clone(),
8230 style.message.clone().with_font_size(font_size),
8231 )
8232 .with_highlights(highlights.clone())
8233 .contained()
8234 .with_margin_left(anchor_x)
8235 }))
8236 .aligned()
8237 .left()
8238 .into_any()
8239 })
8240 .with_cursor_style(CursorStyle::PointingHand)
8241 .on_click(MouseButton::Left, move |_, _, cx| {
8242 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8243 })
8244 // We really need to rethink this ID system...
8245 .with_tooltip::<BlockContextToolip>(
8246 cx.block_id,
8247 "Copy diagnostic message".to_string(),
8248 None,
8249 tooltip_style,
8250 cx,
8251 )
8252 .into_any()
8253 })
8254}
8255
8256pub fn highlight_diagnostic_message(
8257 initial_highlights: Vec<usize>,
8258 message: &str,
8259) -> (String, Vec<usize>) {
8260 let mut message_without_backticks = String::new();
8261 let mut prev_offset = 0;
8262 let mut inside_block = false;
8263 let mut highlights = initial_highlights;
8264 for (match_ix, (offset, _)) in message
8265 .match_indices('`')
8266 .chain([(message.len(), "")])
8267 .enumerate()
8268 {
8269 message_without_backticks.push_str(&message[prev_offset..offset]);
8270 if inside_block {
8271 highlights.extend(prev_offset - match_ix..offset - match_ix);
8272 }
8273
8274 inside_block = !inside_block;
8275 prev_offset = offset + 1;
8276 }
8277
8278 (message_without_backticks, highlights)
8279}
8280
8281pub fn diagnostic_style(
8282 severity: DiagnosticSeverity,
8283 valid: bool,
8284 theme: &theme::Editor,
8285) -> DiagnosticStyle {
8286 match (severity, valid) {
8287 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8288 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8289 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8290 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8291 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8292 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8293 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8294 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8295 _ => theme.invalid_hint_diagnostic.clone(),
8296 }
8297}
8298
8299pub fn combine_syntax_and_fuzzy_match_highlights(
8300 text: &str,
8301 default_style: HighlightStyle,
8302 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8303 match_indices: &[usize],
8304) -> Vec<(Range<usize>, HighlightStyle)> {
8305 let mut result = Vec::new();
8306 let mut match_indices = match_indices.iter().copied().peekable();
8307
8308 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8309 {
8310 syntax_highlight.weight = None;
8311
8312 // Add highlights for any fuzzy match characters before the next
8313 // syntax highlight range.
8314 while let Some(&match_index) = match_indices.peek() {
8315 if match_index >= range.start {
8316 break;
8317 }
8318 match_indices.next();
8319 let end_index = char_ix_after(match_index, text);
8320 let mut match_style = default_style;
8321 match_style.weight = Some(fonts::Weight::BOLD);
8322 result.push((match_index..end_index, match_style));
8323 }
8324
8325 if range.start == usize::MAX {
8326 break;
8327 }
8328
8329 // Add highlights for any fuzzy match characters within the
8330 // syntax highlight range.
8331 let mut offset = range.start;
8332 while let Some(&match_index) = match_indices.peek() {
8333 if match_index >= range.end {
8334 break;
8335 }
8336
8337 match_indices.next();
8338 if match_index > offset {
8339 result.push((offset..match_index, syntax_highlight));
8340 }
8341
8342 let mut end_index = char_ix_after(match_index, text);
8343 while let Some(&next_match_index) = match_indices.peek() {
8344 if next_match_index == end_index && next_match_index < range.end {
8345 end_index = char_ix_after(next_match_index, text);
8346 match_indices.next();
8347 } else {
8348 break;
8349 }
8350 }
8351
8352 let mut match_style = syntax_highlight;
8353 match_style.weight = Some(fonts::Weight::BOLD);
8354 result.push((match_index..end_index, match_style));
8355 offset = end_index;
8356 }
8357
8358 if offset < range.end {
8359 result.push((offset..range.end, syntax_highlight));
8360 }
8361 }
8362
8363 fn char_ix_after(ix: usize, text: &str) -> usize {
8364 ix + text[ix..].chars().next().unwrap().len_utf8()
8365 }
8366
8367 result
8368}
8369
8370pub fn styled_runs_for_code_label<'a>(
8371 label: &'a CodeLabel,
8372 syntax_theme: &'a theme::SyntaxTheme,
8373) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8374 let fade_out = HighlightStyle {
8375 fade_out: Some(0.35),
8376 ..Default::default()
8377 };
8378
8379 let mut prev_end = label.filter_range.end;
8380 label
8381 .runs
8382 .iter()
8383 .enumerate()
8384 .flat_map(move |(ix, (range, highlight_id))| {
8385 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8386 style
8387 } else {
8388 return Default::default();
8389 };
8390 let mut muted_style = style;
8391 muted_style.highlight(fade_out);
8392
8393 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8394 if range.start >= label.filter_range.end {
8395 if range.start > prev_end {
8396 runs.push((prev_end..range.start, fade_out));
8397 }
8398 runs.push((range.clone(), muted_style));
8399 } else if range.end <= label.filter_range.end {
8400 runs.push((range.clone(), style));
8401 } else {
8402 runs.push((range.start..label.filter_range.end, style));
8403 runs.push((label.filter_range.end..range.end, muted_style));
8404 }
8405 prev_end = cmp::max(prev_end, range.end);
8406
8407 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8408 runs.push((prev_end..label.text.len(), fade_out));
8409 }
8410
8411 runs
8412 })
8413}
8414
8415pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8416 let mut index = 0;
8417 let mut codepoints = text.char_indices().peekable();
8418
8419 std::iter::from_fn(move || {
8420 let start_index = index;
8421 while let Some((new_index, codepoint)) = codepoints.next() {
8422 index = new_index + codepoint.len_utf8();
8423 let current_upper = codepoint.is_uppercase();
8424 let next_upper = codepoints
8425 .peek()
8426 .map(|(_, c)| c.is_uppercase())
8427 .unwrap_or(false);
8428
8429 if !current_upper && next_upper {
8430 return Some(&text[start_index..index]);
8431 }
8432 }
8433
8434 index = text.len();
8435 if start_index < text.len() {
8436 return Some(&text[start_index..]);
8437 }
8438 None
8439 })
8440 .flat_map(|word| word.split_inclusive('_'))
8441}
8442
8443trait RangeToAnchorExt {
8444 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8445}
8446
8447impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8448 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8449 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8450 }
8451}