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