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