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