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