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