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