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