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