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