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