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