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