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