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