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