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