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