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