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