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