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