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