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 its behavior.
15pub mod actions;
16mod blame_entry_tooltip;
17mod blink_manager;
18mod clangd_ext;
19mod debounced_delay;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod hunk_diff;
29mod indent_guides;
30mod inlay_hint_cache;
31mod inline_completion_provider;
32pub mod items;
33mod linked_editing_ranges;
34mod lsp_ext;
35mod mouse_context_menu;
36pub mod movement;
37mod persistence;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45mod signature_help;
46#[cfg(any(test, feature = "test-support"))]
47pub mod test;
48
49use ::git::diff::{DiffHunk, DiffHunkStatus};
50use ::git::{parse_git_remote_url, BuildPermalinkParams, GitHostingProviderRegistry};
51pub(crate) use actions::*;
52use aho_corasick::AhoCorasick;
53use anyhow::{anyhow, Context as _, Result};
54use blink_manager::BlinkManager;
55use client::{Collaborator, ParticipantIndex};
56use clock::ReplicaId;
57use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
58use convert_case::{Case, Casing};
59use debounced_delay::DebouncedDelay;
60use display_map::*;
61pub use display_map::{DisplayPoint, FoldPlaceholder};
62pub use editor_settings::{
63 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings,
64};
65pub use editor_settings_controls::*;
66use element::LineWithInvisibles;
67pub use element::{
68 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
69};
70use futures::FutureExt;
71use fuzzy::{StringMatch, StringMatchCandidate};
72use git::blame::GitBlame;
73use git::diff_hunk_to_display;
74use gpui::{
75 div, impl_actions, point, prelude::*, px, relative, size, uniform_list, Action, AnyElement,
76 AppContext, AsyncWindowContext, AvailableSpace, BackgroundExecutor, Bounds, ClipboardEntry,
77 ClipboardItem, Context, DispatchPhase, ElementId, EntityId, EventEmitter, FocusHandle,
78 FocusOutEvent, FocusableView, FontId, FontWeight, HighlightStyle, Hsla, InteractiveText,
79 KeyContext, ListSizingBehavior, Model, MouseButton, PaintQuad, ParentElement, Pixels, Render,
80 SharedString, Size, StrikethroughStyle, Styled, StyledText, Subscription, Task, TextStyle,
81 UTF16Selection, UnderlineStyle, UniformListScrollHandle, View, ViewContext, ViewInputHandler,
82 VisualContext, WeakFocusHandle, WeakView, WindowContext,
83};
84use highlight_matching_bracket::refresh_matching_bracket_highlights;
85use hover_popover::{hide_hover, HoverState};
86use hunk_diff::ExpandedHunks;
87pub(crate) use hunk_diff::HoveredHunk;
88use indent_guides::ActiveIndentGuidesState;
89use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
90pub use inline_completion_provider::*;
91pub use items::MAX_TAB_TITLE_LEN;
92use itertools::Itertools;
93use language::{
94 language_settings::{self, all_language_settings, InlayHintSettings},
95 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
96 CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
97 Point, Selection, SelectionGoal, TransactionId,
98};
99use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
100use linked_editing_ranges::refresh_linked_ranges;
101use similar::{ChangeTag, TextDiff};
102use task::{ResolvedTask, TaskTemplate, TaskVariables};
103
104use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
105pub use lsp::CompletionContext;
106use lsp::{
107 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
108 LanguageServerId,
109};
110use mouse_context_menu::MouseContextMenu;
111use movement::TextLayoutDetails;
112pub use multi_buffer::{
113 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
114 ToPoint,
115};
116use multi_buffer::{ExpandExcerptDirection, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16};
117use ordered_float::OrderedFloat;
118use parking_lot::{Mutex, RwLock};
119use project::project_settings::{GitGutterSetting, ProjectSettings};
120use project::{
121 CodeAction, Completion, CompletionIntent, FormatTrigger, Item, Location, Project, ProjectPath,
122 ProjectTransaction, TaskSourceKind,
123};
124use rand::prelude::*;
125use rpc::{proto::*, ErrorExt};
126use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
127use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
128use serde::{Deserialize, Serialize};
129use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
130use smallvec::SmallVec;
131use snippet::Snippet;
132use std::{
133 any::TypeId,
134 borrow::Cow,
135 cell::RefCell,
136 cmp::{self, Ordering, Reverse},
137 mem,
138 num::NonZeroU32,
139 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
140 path::{Path, PathBuf},
141 rc::Rc,
142 sync::Arc,
143 time::{Duration, Instant},
144};
145pub use sum_tree::Bias;
146use sum_tree::TreeMap;
147use text::{BufferId, OffsetUtf16, Rope};
148use theme::{
149 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
150 ThemeColors, ThemeSettings,
151};
152use ui::{
153 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
154 ListItem, Popover, Tooltip,
155};
156use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
157use workspace::item::{ItemHandle, PreviewTabsSettings};
158use workspace::notifications::{DetachAndPromptErr, NotificationId};
159use workspace::{
160 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
161};
162use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
163
164use crate::hover_links::find_url;
165use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
166
167pub const FILE_HEADER_HEIGHT: u32 = 1;
168pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
169pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
170pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
171const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
172const MAX_LINE_LEN: usize = 1024;
173const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
174const MAX_SELECTION_HISTORY_LEN: usize = 1024;
175pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
176#[doc(hidden)]
177pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
178#[doc(hidden)]
179pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
180
181pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
182pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
183
184pub fn render_parsed_markdown(
185 element_id: impl Into<ElementId>,
186 parsed: &language::ParsedMarkdown,
187 editor_style: &EditorStyle,
188 workspace: Option<WeakView<Workspace>>,
189 cx: &mut WindowContext,
190) -> InteractiveText {
191 let code_span_background_color = cx
192 .theme()
193 .colors()
194 .editor_document_highlight_read_background;
195
196 let highlights = gpui::combine_highlights(
197 parsed.highlights.iter().filter_map(|(range, highlight)| {
198 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
199 Some((range.clone(), highlight))
200 }),
201 parsed
202 .regions
203 .iter()
204 .zip(&parsed.region_ranges)
205 .filter_map(|(region, range)| {
206 if region.code {
207 Some((
208 range.clone(),
209 HighlightStyle {
210 background_color: Some(code_span_background_color),
211 ..Default::default()
212 },
213 ))
214 } else {
215 None
216 }
217 }),
218 );
219
220 let mut links = Vec::new();
221 let mut link_ranges = Vec::new();
222 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
223 if let Some(link) = region.link.clone() {
224 links.push(link);
225 link_ranges.push(range.clone());
226 }
227 }
228
229 InteractiveText::new(
230 element_id,
231 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
232 )
233 .on_click(link_ranges, move |clicked_range_ix, cx| {
234 match &links[clicked_range_ix] {
235 markdown::Link::Web { url } => cx.open_url(url),
236 markdown::Link::Path { path } => {
237 if let Some(workspace) = &workspace {
238 _ = workspace.update(cx, |workspace, cx| {
239 workspace.open_abs_path(path.clone(), false, cx).detach();
240 });
241 }
242 }
243 }
244 })
245}
246
247#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
248pub(crate) enum InlayId {
249 Suggestion(usize),
250 Hint(usize),
251}
252
253impl InlayId {
254 fn id(&self) -> usize {
255 match self {
256 Self::Suggestion(id) => *id,
257 Self::Hint(id) => *id,
258 }
259 }
260}
261
262enum DiffRowHighlight {}
263enum DocumentHighlightRead {}
264enum DocumentHighlightWrite {}
265enum InputComposition {}
266
267#[derive(Copy, Clone, PartialEq, Eq)]
268pub enum Direction {
269 Prev,
270 Next,
271}
272
273#[derive(Debug, Copy, Clone, PartialEq, Eq)]
274pub enum Navigated {
275 Yes,
276 No,
277}
278
279impl Navigated {
280 pub fn from_bool(yes: bool) -> Navigated {
281 if yes {
282 Navigated::Yes
283 } else {
284 Navigated::No
285 }
286 }
287}
288
289pub fn init_settings(cx: &mut AppContext) {
290 EditorSettings::register(cx);
291}
292
293pub fn init(cx: &mut AppContext) {
294 init_settings(cx);
295
296 workspace::register_project_item::<Editor>(cx);
297 workspace::FollowableViewRegistry::register::<Editor>(cx);
298 workspace::register_serializable_item::<Editor>(cx);
299
300 cx.observe_new_views(
301 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
302 workspace.register_action(Editor::new_file);
303 workspace.register_action(Editor::new_file_vertical);
304 workspace.register_action(Editor::new_file_horizontal);
305 },
306 )
307 .detach();
308
309 cx.on_action(move |_: &workspace::NewFile, cx| {
310 let app_state = workspace::AppState::global(cx);
311 if let Some(app_state) = app_state.upgrade() {
312 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
313 Editor::new_file(workspace, &Default::default(), cx)
314 })
315 .detach();
316 }
317 });
318 cx.on_action(move |_: &workspace::NewWindow, cx| {
319 let app_state = workspace::AppState::global(cx);
320 if let Some(app_state) = app_state.upgrade() {
321 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
322 Editor::new_file(workspace, &Default::default(), cx)
323 })
324 .detach();
325 }
326 });
327}
328
329pub struct SearchWithinRange;
330
331trait InvalidationRegion {
332 fn ranges(&self) -> &[Range<Anchor>];
333}
334
335#[derive(Clone, Debug, PartialEq)]
336pub enum SelectPhase {
337 Begin {
338 position: DisplayPoint,
339 add: bool,
340 click_count: usize,
341 },
342 BeginColumnar {
343 position: DisplayPoint,
344 reset: bool,
345 goal_column: u32,
346 },
347 Extend {
348 position: DisplayPoint,
349 click_count: usize,
350 },
351 Update {
352 position: DisplayPoint,
353 goal_column: u32,
354 scroll_delta: gpui::Point<f32>,
355 },
356 End,
357}
358
359#[derive(Clone, Debug)]
360pub enum SelectMode {
361 Character,
362 Word(Range<Anchor>),
363 Line(Range<Anchor>),
364 All,
365}
366
367#[derive(Copy, Clone, PartialEq, Eq, Debug)]
368pub enum EditorMode {
369 SingleLine { auto_width: bool },
370 AutoHeight { max_lines: usize },
371 Full,
372}
373
374#[derive(Clone, Debug)]
375pub enum SoftWrap {
376 None,
377 PreferLine,
378 EditorWidth,
379 Column(u32),
380 Bounded(u32),
381}
382
383#[derive(Clone)]
384pub struct EditorStyle {
385 pub background: Hsla,
386 pub local_player: PlayerColor,
387 pub text: TextStyle,
388 pub scrollbar_width: Pixels,
389 pub syntax: Arc<SyntaxTheme>,
390 pub status: StatusColors,
391 pub inlay_hints_style: HighlightStyle,
392 pub suggestions_style: HighlightStyle,
393 pub unnecessary_code_fade: f32,
394}
395
396impl Default for EditorStyle {
397 fn default() -> Self {
398 Self {
399 background: Hsla::default(),
400 local_player: PlayerColor::default(),
401 text: TextStyle::default(),
402 scrollbar_width: Pixels::default(),
403 syntax: Default::default(),
404 // HACK: Status colors don't have a real default.
405 // We should look into removing the status colors from the editor
406 // style and retrieve them directly from the theme.
407 status: StatusColors::dark(),
408 inlay_hints_style: HighlightStyle::default(),
409 suggestions_style: HighlightStyle::default(),
410 unnecessary_code_fade: Default::default(),
411 }
412 }
413}
414
415type CompletionId = usize;
416
417#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
418struct EditorActionId(usize);
419
420impl EditorActionId {
421 pub fn post_inc(&mut self) -> Self {
422 let answer = self.0;
423
424 *self = Self(answer + 1);
425
426 Self(answer)
427 }
428}
429
430// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
431// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
432
433type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
434type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
435
436#[derive(Default)]
437struct ScrollbarMarkerState {
438 scrollbar_size: Size<Pixels>,
439 dirty: bool,
440 markers: Arc<[PaintQuad]>,
441 pending_refresh: Option<Task<Result<()>>>,
442}
443
444impl ScrollbarMarkerState {
445 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
446 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
447 }
448}
449
450#[derive(Clone, Debug)]
451struct RunnableTasks {
452 templates: Vec<(TaskSourceKind, TaskTemplate)>,
453 offset: MultiBufferOffset,
454 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
455 column: u32,
456 // Values of all named captures, including those starting with '_'
457 extra_variables: HashMap<String, String>,
458 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
459 context_range: Range<BufferOffset>,
460}
461
462#[derive(Clone)]
463struct ResolvedTasks {
464 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
465 position: Anchor,
466}
467#[derive(Copy, Clone, Debug)]
468struct MultiBufferOffset(usize);
469#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
470struct BufferOffset(usize);
471
472// Addons allow storing per-editor state in other crates (e.g. Vim)
473pub trait Addon: 'static {
474 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
475
476 fn to_any(&self) -> &dyn std::any::Any;
477}
478
479/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
480///
481/// See the [module level documentation](self) for more information.
482pub struct Editor {
483 focus_handle: FocusHandle,
484 last_focused_descendant: Option<WeakFocusHandle>,
485 /// The text buffer being edited
486 buffer: Model<MultiBuffer>,
487 /// Map of how text in the buffer should be displayed.
488 /// Handles soft wraps, folds, fake inlay text insertions, etc.
489 pub display_map: Model<DisplayMap>,
490 pub selections: SelectionsCollection,
491 pub scroll_manager: ScrollManager,
492 /// When inline assist editors are linked, they all render cursors because
493 /// typing enters text into each of them, even the ones that aren't focused.
494 pub(crate) show_cursor_when_unfocused: bool,
495 columnar_selection_tail: Option<Anchor>,
496 add_selections_state: Option<AddSelectionsState>,
497 select_next_state: Option<SelectNextState>,
498 select_prev_state: Option<SelectNextState>,
499 selection_history: SelectionHistory,
500 autoclose_regions: Vec<AutocloseRegion>,
501 snippet_stack: InvalidationStack<SnippetState>,
502 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
503 ime_transaction: Option<TransactionId>,
504 active_diagnostics: Option<ActiveDiagnosticGroup>,
505 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
506 project: Option<Model<Project>>,
507 completion_provider: Option<Box<dyn CompletionProvider>>,
508 collaboration_hub: Option<Box<dyn CollaborationHub>>,
509 blink_manager: Model<BlinkManager>,
510 show_cursor_names: bool,
511 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
512 pub show_local_selections: bool,
513 mode: EditorMode,
514 show_breadcrumbs: bool,
515 show_gutter: bool,
516 show_line_numbers: Option<bool>,
517 use_relative_line_numbers: Option<bool>,
518 show_git_diff_gutter: Option<bool>,
519 show_code_actions: Option<bool>,
520 show_runnables: Option<bool>,
521 show_wrap_guides: Option<bool>,
522 show_indent_guides: Option<bool>,
523 placeholder_text: Option<Arc<str>>,
524 highlight_order: usize,
525 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
526 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
527 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
528 scrollbar_marker_state: ScrollbarMarkerState,
529 active_indent_guides_state: ActiveIndentGuidesState,
530 nav_history: Option<ItemNavHistory>,
531 context_menu: RwLock<Option<ContextMenu>>,
532 mouse_context_menu: Option<MouseContextMenu>,
533 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
534 signature_help_state: SignatureHelpState,
535 auto_signature_help: Option<bool>,
536 find_all_references_task_sources: Vec<Anchor>,
537 next_completion_id: CompletionId,
538 completion_documentation_pre_resolve_debounce: DebouncedDelay,
539 available_code_actions: Option<(Location, Arc<[CodeAction]>)>,
540 code_actions_task: Option<Task<()>>,
541 document_highlights_task: Option<Task<()>>,
542 linked_editing_range_task: Option<Task<Option<()>>>,
543 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
544 pending_rename: Option<RenameState>,
545 searchable: bool,
546 cursor_shape: CursorShape,
547 current_line_highlight: Option<CurrentLineHighlight>,
548 collapse_matches: bool,
549 autoindent_mode: Option<AutoindentMode>,
550 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
551 input_enabled: bool,
552 use_modal_editing: bool,
553 read_only: bool,
554 leader_peer_id: Option<PeerId>,
555 remote_id: Option<ViewId>,
556 hover_state: HoverState,
557 gutter_hovered: bool,
558 hovered_link_state: Option<HoveredLinkState>,
559 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
560 active_inline_completion: Option<(Inlay, Option<Range<Anchor>>)>,
561 // enable_inline_completions is a switch that Vim can use to disable
562 // inline completions based on its mode.
563 enable_inline_completions: bool,
564 show_inline_completions_override: Option<bool>,
565 inlay_hint_cache: InlayHintCache,
566 expanded_hunks: ExpandedHunks,
567 next_inlay_id: usize,
568 _subscriptions: Vec<Subscription>,
569 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
570 gutter_dimensions: GutterDimensions,
571 style: Option<EditorStyle>,
572 next_editor_action_id: EditorActionId,
573 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
574 use_autoclose: bool,
575 use_auto_surround: bool,
576 auto_replace_emoji_shortcode: bool,
577 show_git_blame_gutter: bool,
578 show_git_blame_inline: bool,
579 show_git_blame_inline_delay_task: Option<Task<()>>,
580 git_blame_inline_enabled: bool,
581 serialize_dirty_buffers: bool,
582 show_selection_menu: Option<bool>,
583 blame: Option<Model<GitBlame>>,
584 blame_subscription: Option<Subscription>,
585 custom_context_menu: Option<
586 Box<
587 dyn 'static
588 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
589 >,
590 >,
591 last_bounds: Option<Bounds<Pixels>>,
592 expect_bounds_change: Option<Bounds<Pixels>>,
593 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
594 tasks_update_task: Option<Task<()>>,
595 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
596 file_header_size: u32,
597 breadcrumb_header: Option<String>,
598 focused_block: Option<FocusedBlock>,
599 next_scroll_position: NextScrollCursorCenterTopBottom,
600 addons: HashMap<TypeId, Box<dyn Addon>>,
601 _scroll_cursor_center_top_bottom_task: Task<()>,
602}
603
604#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
605enum NextScrollCursorCenterTopBottom {
606 #[default]
607 Center,
608 Top,
609 Bottom,
610}
611
612impl NextScrollCursorCenterTopBottom {
613 fn next(&self) -> Self {
614 match self {
615 Self::Center => Self::Top,
616 Self::Top => Self::Bottom,
617 Self::Bottom => Self::Center,
618 }
619 }
620}
621
622#[derive(Clone)]
623pub struct EditorSnapshot {
624 pub mode: EditorMode,
625 show_gutter: bool,
626 show_line_numbers: Option<bool>,
627 show_git_diff_gutter: Option<bool>,
628 show_code_actions: Option<bool>,
629 show_runnables: Option<bool>,
630 render_git_blame_gutter: bool,
631 pub display_snapshot: DisplaySnapshot,
632 pub placeholder_text: Option<Arc<str>>,
633 is_focused: bool,
634 scroll_anchor: ScrollAnchor,
635 ongoing_scroll: OngoingScroll,
636 current_line_highlight: CurrentLineHighlight,
637 gutter_hovered: bool,
638}
639
640const GIT_BLAME_GUTTER_WIDTH_CHARS: f32 = 53.;
641
642#[derive(Default, Debug, Clone, Copy)]
643pub struct GutterDimensions {
644 pub left_padding: Pixels,
645 pub right_padding: Pixels,
646 pub width: Pixels,
647 pub margin: Pixels,
648 pub git_blame_entries_width: Option<Pixels>,
649}
650
651impl GutterDimensions {
652 /// The full width of the space taken up by the gutter.
653 pub fn full_width(&self) -> Pixels {
654 self.margin + self.width
655 }
656
657 /// The width of the space reserved for the fold indicators,
658 /// use alongside 'justify_end' and `gutter_width` to
659 /// right align content with the line numbers
660 pub fn fold_area_width(&self) -> Pixels {
661 self.margin + self.right_padding
662 }
663}
664
665#[derive(Debug)]
666pub struct RemoteSelection {
667 pub replica_id: ReplicaId,
668 pub selection: Selection<Anchor>,
669 pub cursor_shape: CursorShape,
670 pub peer_id: PeerId,
671 pub line_mode: bool,
672 pub participant_index: Option<ParticipantIndex>,
673 pub user_name: Option<SharedString>,
674}
675
676#[derive(Clone, Debug)]
677struct SelectionHistoryEntry {
678 selections: Arc<[Selection<Anchor>]>,
679 select_next_state: Option<SelectNextState>,
680 select_prev_state: Option<SelectNextState>,
681 add_selections_state: Option<AddSelectionsState>,
682}
683
684enum SelectionHistoryMode {
685 Normal,
686 Undoing,
687 Redoing,
688}
689
690#[derive(Clone, PartialEq, Eq, Hash)]
691struct HoveredCursor {
692 replica_id: u16,
693 selection_id: usize,
694}
695
696impl Default for SelectionHistoryMode {
697 fn default() -> Self {
698 Self::Normal
699 }
700}
701
702#[derive(Default)]
703struct SelectionHistory {
704 #[allow(clippy::type_complexity)]
705 selections_by_transaction:
706 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
707 mode: SelectionHistoryMode,
708 undo_stack: VecDeque<SelectionHistoryEntry>,
709 redo_stack: VecDeque<SelectionHistoryEntry>,
710}
711
712impl SelectionHistory {
713 fn insert_transaction(
714 &mut self,
715 transaction_id: TransactionId,
716 selections: Arc<[Selection<Anchor>]>,
717 ) {
718 self.selections_by_transaction
719 .insert(transaction_id, (selections, None));
720 }
721
722 #[allow(clippy::type_complexity)]
723 fn transaction(
724 &self,
725 transaction_id: TransactionId,
726 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
727 self.selections_by_transaction.get(&transaction_id)
728 }
729
730 #[allow(clippy::type_complexity)]
731 fn transaction_mut(
732 &mut self,
733 transaction_id: TransactionId,
734 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
735 self.selections_by_transaction.get_mut(&transaction_id)
736 }
737
738 fn push(&mut self, entry: SelectionHistoryEntry) {
739 if !entry.selections.is_empty() {
740 match self.mode {
741 SelectionHistoryMode::Normal => {
742 self.push_undo(entry);
743 self.redo_stack.clear();
744 }
745 SelectionHistoryMode::Undoing => self.push_redo(entry),
746 SelectionHistoryMode::Redoing => self.push_undo(entry),
747 }
748 }
749 }
750
751 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
752 if self
753 .undo_stack
754 .back()
755 .map_or(true, |e| e.selections != entry.selections)
756 {
757 self.undo_stack.push_back(entry);
758 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
759 self.undo_stack.pop_front();
760 }
761 }
762 }
763
764 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
765 if self
766 .redo_stack
767 .back()
768 .map_or(true, |e| e.selections != entry.selections)
769 {
770 self.redo_stack.push_back(entry);
771 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
772 self.redo_stack.pop_front();
773 }
774 }
775 }
776}
777
778struct RowHighlight {
779 index: usize,
780 range: RangeInclusive<Anchor>,
781 color: Option<Hsla>,
782 should_autoscroll: bool,
783}
784
785#[derive(Clone, Debug)]
786struct AddSelectionsState {
787 above: bool,
788 stack: Vec<usize>,
789}
790
791#[derive(Clone)]
792struct SelectNextState {
793 query: AhoCorasick,
794 wordwise: bool,
795 done: bool,
796}
797
798impl std::fmt::Debug for SelectNextState {
799 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
800 f.debug_struct(std::any::type_name::<Self>())
801 .field("wordwise", &self.wordwise)
802 .field("done", &self.done)
803 .finish()
804 }
805}
806
807#[derive(Debug)]
808struct AutocloseRegion {
809 selection_id: usize,
810 range: Range<Anchor>,
811 pair: BracketPair,
812}
813
814#[derive(Debug)]
815struct SnippetState {
816 ranges: Vec<Vec<Range<Anchor>>>,
817 active_index: usize,
818}
819
820#[doc(hidden)]
821pub struct RenameState {
822 pub range: Range<Anchor>,
823 pub old_name: Arc<str>,
824 pub editor: View<Editor>,
825 block_id: CustomBlockId,
826}
827
828struct InvalidationStack<T>(Vec<T>);
829
830struct RegisteredInlineCompletionProvider {
831 provider: Arc<dyn InlineCompletionProviderHandle>,
832 _subscription: Subscription,
833}
834
835enum ContextMenu {
836 Completions(CompletionsMenu),
837 CodeActions(CodeActionsMenu),
838}
839
840impl ContextMenu {
841 fn select_first(
842 &mut self,
843 project: Option<&Model<Project>>,
844 cx: &mut ViewContext<Editor>,
845 ) -> bool {
846 if self.visible() {
847 match self {
848 ContextMenu::Completions(menu) => menu.select_first(project, cx),
849 ContextMenu::CodeActions(menu) => menu.select_first(cx),
850 }
851 true
852 } else {
853 false
854 }
855 }
856
857 fn select_prev(
858 &mut self,
859 project: Option<&Model<Project>>,
860 cx: &mut ViewContext<Editor>,
861 ) -> bool {
862 if self.visible() {
863 match self {
864 ContextMenu::Completions(menu) => menu.select_prev(project, cx),
865 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
866 }
867 true
868 } else {
869 false
870 }
871 }
872
873 fn select_next(
874 &mut self,
875 project: Option<&Model<Project>>,
876 cx: &mut ViewContext<Editor>,
877 ) -> bool {
878 if self.visible() {
879 match self {
880 ContextMenu::Completions(menu) => menu.select_next(project, cx),
881 ContextMenu::CodeActions(menu) => menu.select_next(cx),
882 }
883 true
884 } else {
885 false
886 }
887 }
888
889 fn select_last(
890 &mut self,
891 project: Option<&Model<Project>>,
892 cx: &mut ViewContext<Editor>,
893 ) -> bool {
894 if self.visible() {
895 match self {
896 ContextMenu::Completions(menu) => menu.select_last(project, cx),
897 ContextMenu::CodeActions(menu) => menu.select_last(cx),
898 }
899 true
900 } else {
901 false
902 }
903 }
904
905 fn visible(&self) -> bool {
906 match self {
907 ContextMenu::Completions(menu) => menu.visible(),
908 ContextMenu::CodeActions(menu) => menu.visible(),
909 }
910 }
911
912 fn render(
913 &self,
914 cursor_position: DisplayPoint,
915 style: &EditorStyle,
916 max_height: Pixels,
917 workspace: Option<WeakView<Workspace>>,
918 cx: &mut ViewContext<Editor>,
919 ) -> (ContextMenuOrigin, AnyElement) {
920 match self {
921 ContextMenu::Completions(menu) => (
922 ContextMenuOrigin::EditorPoint(cursor_position),
923 menu.render(style, max_height, workspace, cx),
924 ),
925 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, max_height, cx),
926 }
927 }
928}
929
930enum ContextMenuOrigin {
931 EditorPoint(DisplayPoint),
932 GutterIndicator(DisplayRow),
933}
934
935#[derive(Clone)]
936struct CompletionsMenu {
937 id: CompletionId,
938 sort_completions: bool,
939 initial_position: Anchor,
940 buffer: Model<Buffer>,
941 completions: Arc<RwLock<Box<[Completion]>>>,
942 match_candidates: Arc<[StringMatchCandidate]>,
943 matches: Arc<[StringMatch]>,
944 selected_item: usize,
945 scroll_handle: UniformListScrollHandle,
946 selected_completion_documentation_resolve_debounce: Arc<Mutex<DebouncedDelay>>,
947}
948
949impl CompletionsMenu {
950 fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
951 self.selected_item = 0;
952 self.scroll_handle.scroll_to_item(self.selected_item);
953 self.attempt_resolve_selected_completion_documentation(project, cx);
954 cx.notify();
955 }
956
957 fn select_prev(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
958 if self.selected_item > 0 {
959 self.selected_item -= 1;
960 } else {
961 self.selected_item = self.matches.len() - 1;
962 }
963 self.scroll_handle.scroll_to_item(self.selected_item);
964 self.attempt_resolve_selected_completion_documentation(project, cx);
965 cx.notify();
966 }
967
968 fn select_next(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
969 if self.selected_item + 1 < self.matches.len() {
970 self.selected_item += 1;
971 } else {
972 self.selected_item = 0;
973 }
974 self.scroll_handle.scroll_to_item(self.selected_item);
975 self.attempt_resolve_selected_completion_documentation(project, cx);
976 cx.notify();
977 }
978
979 fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
980 self.selected_item = self.matches.len() - 1;
981 self.scroll_handle.scroll_to_item(self.selected_item);
982 self.attempt_resolve_selected_completion_documentation(project, cx);
983 cx.notify();
984 }
985
986 fn pre_resolve_completion_documentation(
987 buffer: Model<Buffer>,
988 completions: Arc<RwLock<Box<[Completion]>>>,
989 matches: Arc<[StringMatch]>,
990 editor: &Editor,
991 cx: &mut ViewContext<Editor>,
992 ) -> Task<()> {
993 let settings = EditorSettings::get_global(cx);
994 if !settings.show_completion_documentation {
995 return Task::ready(());
996 }
997
998 let Some(provider) = editor.completion_provider.as_ref() else {
999 return Task::ready(());
1000 };
1001
1002 let resolve_task = provider.resolve_completions(
1003 buffer,
1004 matches.iter().map(|m| m.candidate_id).collect(),
1005 completions.clone(),
1006 cx,
1007 );
1008
1009 cx.spawn(move |this, mut cx| async move {
1010 if let Some(true) = resolve_task.await.log_err() {
1011 this.update(&mut cx, |_, cx| cx.notify()).ok();
1012 }
1013 })
1014 }
1015
1016 fn attempt_resolve_selected_completion_documentation(
1017 &mut self,
1018 project: Option<&Model<Project>>,
1019 cx: &mut ViewContext<Editor>,
1020 ) {
1021 let settings = EditorSettings::get_global(cx);
1022 if !settings.show_completion_documentation {
1023 return;
1024 }
1025
1026 let completion_index = self.matches[self.selected_item].candidate_id;
1027 let Some(project) = project else {
1028 return;
1029 };
1030
1031 let resolve_task = project.update(cx, |project, cx| {
1032 project.resolve_completions(
1033 self.buffer.clone(),
1034 vec![completion_index],
1035 self.completions.clone(),
1036 cx,
1037 )
1038 });
1039
1040 let delay_ms =
1041 EditorSettings::get_global(cx).completion_documentation_secondary_query_debounce;
1042 let delay = Duration::from_millis(delay_ms);
1043
1044 self.selected_completion_documentation_resolve_debounce
1045 .lock()
1046 .fire_new(delay, cx, |_, cx| {
1047 cx.spawn(move |this, mut cx| async move {
1048 if let Some(true) = resolve_task.await.log_err() {
1049 this.update(&mut cx, |_, cx| cx.notify()).ok();
1050 }
1051 })
1052 });
1053 }
1054
1055 fn visible(&self) -> bool {
1056 !self.matches.is_empty()
1057 }
1058
1059 fn render(
1060 &self,
1061 style: &EditorStyle,
1062 max_height: Pixels,
1063 workspace: Option<WeakView<Workspace>>,
1064 cx: &mut ViewContext<Editor>,
1065 ) -> AnyElement {
1066 let settings = EditorSettings::get_global(cx);
1067 let show_completion_documentation = settings.show_completion_documentation;
1068
1069 let widest_completion_ix = self
1070 .matches
1071 .iter()
1072 .enumerate()
1073 .max_by_key(|(_, mat)| {
1074 let completions = self.completions.read();
1075 let completion = &completions[mat.candidate_id];
1076 let documentation = &completion.documentation;
1077
1078 let mut len = completion.label.text.chars().count();
1079 if let Some(Documentation::SingleLine(text)) = documentation {
1080 if show_completion_documentation {
1081 len += text.chars().count();
1082 }
1083 }
1084
1085 len
1086 })
1087 .map(|(ix, _)| ix);
1088
1089 let completions = self.completions.clone();
1090 let matches = self.matches.clone();
1091 let selected_item = self.selected_item;
1092 let style = style.clone();
1093
1094 let multiline_docs = if show_completion_documentation {
1095 let mat = &self.matches[selected_item];
1096 let multiline_docs = match &self.completions.read()[mat.candidate_id].documentation {
1097 Some(Documentation::MultiLinePlainText(text)) => {
1098 Some(div().child(SharedString::from(text.clone())))
1099 }
1100 Some(Documentation::MultiLineMarkdown(parsed)) if !parsed.text.is_empty() => {
1101 Some(div().child(render_parsed_markdown(
1102 "completions_markdown",
1103 parsed,
1104 &style,
1105 workspace,
1106 cx,
1107 )))
1108 }
1109 _ => None,
1110 };
1111 multiline_docs.map(|div| {
1112 div.id("multiline_docs")
1113 .max_h(max_height)
1114 .flex_1()
1115 .px_1p5()
1116 .py_1()
1117 .min_w(px(260.))
1118 .max_w(px(640.))
1119 .w(px(500.))
1120 .overflow_y_scroll()
1121 .occlude()
1122 })
1123 } else {
1124 None
1125 };
1126
1127 let list = uniform_list(
1128 cx.view().clone(),
1129 "completions",
1130 matches.len(),
1131 move |_editor, range, cx| {
1132 let start_ix = range.start;
1133 let completions_guard = completions.read();
1134
1135 matches[range]
1136 .iter()
1137 .enumerate()
1138 .map(|(ix, mat)| {
1139 let item_ix = start_ix + ix;
1140 let candidate_id = mat.candidate_id;
1141 let completion = &completions_guard[candidate_id];
1142
1143 let documentation = if show_completion_documentation {
1144 &completion.documentation
1145 } else {
1146 &None
1147 };
1148
1149 let highlights = gpui::combine_highlights(
1150 mat.ranges().map(|range| (range, FontWeight::BOLD.into())),
1151 styled_runs_for_code_label(&completion.label, &style.syntax).map(
1152 |(range, mut highlight)| {
1153 // Ignore font weight for syntax highlighting, as we'll use it
1154 // for fuzzy matches.
1155 highlight.font_weight = None;
1156
1157 if completion.lsp_completion.deprecated.unwrap_or(false) {
1158 highlight.strikethrough = Some(StrikethroughStyle {
1159 thickness: 1.0.into(),
1160 ..Default::default()
1161 });
1162 highlight.color = Some(cx.theme().colors().text_muted);
1163 }
1164
1165 (range, highlight)
1166 },
1167 ),
1168 );
1169 let completion_label = StyledText::new(completion.label.text.clone())
1170 .with_highlights(&style.text, highlights);
1171 let documentation_label =
1172 if let Some(Documentation::SingleLine(text)) = documentation {
1173 if text.trim().is_empty() {
1174 None
1175 } else {
1176 Some(
1177 Label::new(text.clone())
1178 .ml_4()
1179 .size(LabelSize::Small)
1180 .color(Color::Muted),
1181 )
1182 }
1183 } else {
1184 None
1185 };
1186
1187 div().min_w(px(220.)).max_w(px(540.)).child(
1188 ListItem::new(mat.candidate_id)
1189 .inset(true)
1190 .selected(item_ix == selected_item)
1191 .on_click(cx.listener(move |editor, _event, cx| {
1192 cx.stop_propagation();
1193 if let Some(task) = editor.confirm_completion(
1194 &ConfirmCompletion {
1195 item_ix: Some(item_ix),
1196 },
1197 cx,
1198 ) {
1199 task.detach_and_log_err(cx)
1200 }
1201 }))
1202 .child(h_flex().overflow_hidden().child(completion_label))
1203 .end_slot::<Label>(documentation_label),
1204 )
1205 })
1206 .collect()
1207 },
1208 )
1209 .occlude()
1210 .max_h(max_height)
1211 .track_scroll(self.scroll_handle.clone())
1212 .with_width_from_item(widest_completion_ix)
1213 .with_sizing_behavior(ListSizingBehavior::Infer);
1214
1215 Popover::new()
1216 .child(list)
1217 .when_some(multiline_docs, |popover, multiline_docs| {
1218 popover.aside(multiline_docs)
1219 })
1220 .into_any_element()
1221 }
1222
1223 pub async fn filter(&mut self, query: Option<&str>, executor: BackgroundExecutor) {
1224 let mut matches = if let Some(query) = query {
1225 fuzzy::match_strings(
1226 &self.match_candidates,
1227 query,
1228 query.chars().any(|c| c.is_uppercase()),
1229 100,
1230 &Default::default(),
1231 executor,
1232 )
1233 .await
1234 } else {
1235 self.match_candidates
1236 .iter()
1237 .enumerate()
1238 .map(|(candidate_id, candidate)| StringMatch {
1239 candidate_id,
1240 score: Default::default(),
1241 positions: Default::default(),
1242 string: candidate.string.clone(),
1243 })
1244 .collect()
1245 };
1246
1247 // Remove all candidates where the query's start does not match the start of any word in the candidate
1248 if let Some(query) = query {
1249 if let Some(query_start) = query.chars().next() {
1250 matches.retain(|string_match| {
1251 split_words(&string_match.string).any(|word| {
1252 // Check that the first codepoint of the word as lowercase matches the first
1253 // codepoint of the query as lowercase
1254 word.chars()
1255 .flat_map(|codepoint| codepoint.to_lowercase())
1256 .zip(query_start.to_lowercase())
1257 .all(|(word_cp, query_cp)| word_cp == query_cp)
1258 })
1259 });
1260 }
1261 }
1262
1263 let completions = self.completions.read();
1264 if self.sort_completions {
1265 matches.sort_unstable_by_key(|mat| {
1266 // We do want to strike a balance here between what the language server tells us
1267 // to sort by (the sort_text) and what are "obvious" good matches (i.e. when you type
1268 // `Creat` and there is a local variable called `CreateComponent`).
1269 // So what we do is: we bucket all matches into two buckets
1270 // - Strong matches
1271 // - Weak matches
1272 // Strong matches are the ones with a high fuzzy-matcher score (the "obvious" matches)
1273 // and the Weak matches are the rest.
1274 //
1275 // For the strong matches, we sort by the language-servers score first and for the weak
1276 // matches, we prefer our fuzzy finder first.
1277 //
1278 // The thinking behind that: it's useless to take the sort_text the language-server gives
1279 // us into account when it's obviously a bad match.
1280
1281 #[derive(PartialEq, Eq, PartialOrd, Ord)]
1282 enum MatchScore<'a> {
1283 Strong {
1284 sort_text: Option<&'a str>,
1285 score: Reverse<OrderedFloat<f64>>,
1286 sort_key: (usize, &'a str),
1287 },
1288 Weak {
1289 score: Reverse<OrderedFloat<f64>>,
1290 sort_text: Option<&'a str>,
1291 sort_key: (usize, &'a str),
1292 },
1293 }
1294
1295 let completion = &completions[mat.candidate_id];
1296 let sort_key = completion.sort_key();
1297 let sort_text = completion.lsp_completion.sort_text.as_deref();
1298 let score = Reverse(OrderedFloat(mat.score));
1299
1300 if mat.score >= 0.2 {
1301 MatchScore::Strong {
1302 sort_text,
1303 score,
1304 sort_key,
1305 }
1306 } else {
1307 MatchScore::Weak {
1308 score,
1309 sort_text,
1310 sort_key,
1311 }
1312 }
1313 });
1314 }
1315
1316 for mat in &mut matches {
1317 let completion = &completions[mat.candidate_id];
1318 mat.string.clone_from(&completion.label.text);
1319 for position in &mut mat.positions {
1320 *position += completion.label.filter_range.start;
1321 }
1322 }
1323 drop(completions);
1324
1325 self.matches = matches.into();
1326 self.selected_item = 0;
1327 }
1328}
1329
1330#[derive(Clone)]
1331struct CodeActionContents {
1332 tasks: Option<Arc<ResolvedTasks>>,
1333 actions: Option<Arc<[CodeAction]>>,
1334}
1335
1336impl CodeActionContents {
1337 fn len(&self) -> usize {
1338 match (&self.tasks, &self.actions) {
1339 (Some(tasks), Some(actions)) => actions.len() + tasks.templates.len(),
1340 (Some(tasks), None) => tasks.templates.len(),
1341 (None, Some(actions)) => actions.len(),
1342 (None, None) => 0,
1343 }
1344 }
1345
1346 fn is_empty(&self) -> bool {
1347 match (&self.tasks, &self.actions) {
1348 (Some(tasks), Some(actions)) => actions.is_empty() && tasks.templates.is_empty(),
1349 (Some(tasks), None) => tasks.templates.is_empty(),
1350 (None, Some(actions)) => actions.is_empty(),
1351 (None, None) => true,
1352 }
1353 }
1354
1355 fn iter(&self) -> impl Iterator<Item = CodeActionsItem> + '_ {
1356 self.tasks
1357 .iter()
1358 .flat_map(|tasks| {
1359 tasks
1360 .templates
1361 .iter()
1362 .map(|(kind, task)| CodeActionsItem::Task(kind.clone(), task.clone()))
1363 })
1364 .chain(self.actions.iter().flat_map(|actions| {
1365 actions
1366 .iter()
1367 .map(|action| CodeActionsItem::CodeAction(action.clone()))
1368 }))
1369 }
1370 fn get(&self, index: usize) -> Option<CodeActionsItem> {
1371 match (&self.tasks, &self.actions) {
1372 (Some(tasks), Some(actions)) => {
1373 if index < tasks.templates.len() {
1374 tasks
1375 .templates
1376 .get(index)
1377 .cloned()
1378 .map(|(kind, task)| CodeActionsItem::Task(kind, task))
1379 } else {
1380 actions
1381 .get(index - tasks.templates.len())
1382 .cloned()
1383 .map(CodeActionsItem::CodeAction)
1384 }
1385 }
1386 (Some(tasks), None) => tasks
1387 .templates
1388 .get(index)
1389 .cloned()
1390 .map(|(kind, task)| CodeActionsItem::Task(kind, task)),
1391 (None, Some(actions)) => actions.get(index).cloned().map(CodeActionsItem::CodeAction),
1392 (None, None) => None,
1393 }
1394 }
1395}
1396
1397#[allow(clippy::large_enum_variant)]
1398#[derive(Clone)]
1399enum CodeActionsItem {
1400 Task(TaskSourceKind, ResolvedTask),
1401 CodeAction(CodeAction),
1402}
1403
1404impl CodeActionsItem {
1405 fn as_task(&self) -> Option<&ResolvedTask> {
1406 let Self::Task(_, task) = self else {
1407 return None;
1408 };
1409 Some(task)
1410 }
1411 fn as_code_action(&self) -> Option<&CodeAction> {
1412 let Self::CodeAction(action) = self else {
1413 return None;
1414 };
1415 Some(action)
1416 }
1417 fn label(&self) -> String {
1418 match self {
1419 Self::CodeAction(action) => action.lsp_action.title.clone(),
1420 Self::Task(_, task) => task.resolved_label.clone(),
1421 }
1422 }
1423}
1424
1425struct CodeActionsMenu {
1426 actions: CodeActionContents,
1427 buffer: Model<Buffer>,
1428 selected_item: usize,
1429 scroll_handle: UniformListScrollHandle,
1430 deployed_from_indicator: Option<DisplayRow>,
1431}
1432
1433impl CodeActionsMenu {
1434 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1435 self.selected_item = 0;
1436 self.scroll_handle.scroll_to_item(self.selected_item);
1437 cx.notify()
1438 }
1439
1440 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1441 if self.selected_item > 0 {
1442 self.selected_item -= 1;
1443 } else {
1444 self.selected_item = self.actions.len() - 1;
1445 }
1446 self.scroll_handle.scroll_to_item(self.selected_item);
1447 cx.notify();
1448 }
1449
1450 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1451 if self.selected_item + 1 < self.actions.len() {
1452 self.selected_item += 1;
1453 } else {
1454 self.selected_item = 0;
1455 }
1456 self.scroll_handle.scroll_to_item(self.selected_item);
1457 cx.notify();
1458 }
1459
1460 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1461 self.selected_item = self.actions.len() - 1;
1462 self.scroll_handle.scroll_to_item(self.selected_item);
1463 cx.notify()
1464 }
1465
1466 fn visible(&self) -> bool {
1467 !self.actions.is_empty()
1468 }
1469
1470 fn render(
1471 &self,
1472 cursor_position: DisplayPoint,
1473 _style: &EditorStyle,
1474 max_height: Pixels,
1475 cx: &mut ViewContext<Editor>,
1476 ) -> (ContextMenuOrigin, AnyElement) {
1477 let actions = self.actions.clone();
1478 let selected_item = self.selected_item;
1479 let element = uniform_list(
1480 cx.view().clone(),
1481 "code_actions_menu",
1482 self.actions.len(),
1483 move |_this, range, cx| {
1484 actions
1485 .iter()
1486 .skip(range.start)
1487 .take(range.end - range.start)
1488 .enumerate()
1489 .map(|(ix, action)| {
1490 let item_ix = range.start + ix;
1491 let selected = selected_item == item_ix;
1492 let colors = cx.theme().colors();
1493 div()
1494 .px_1()
1495 .rounded_md()
1496 .text_color(colors.text)
1497 .when(selected, |style| {
1498 style
1499 .bg(colors.element_active)
1500 .text_color(colors.text_accent)
1501 })
1502 .hover(|style| {
1503 style
1504 .bg(colors.element_hover)
1505 .text_color(colors.text_accent)
1506 })
1507 .whitespace_nowrap()
1508 .when_some(action.as_code_action(), |this, action| {
1509 this.on_mouse_down(
1510 MouseButton::Left,
1511 cx.listener(move |editor, _, cx| {
1512 cx.stop_propagation();
1513 if let Some(task) = editor.confirm_code_action(
1514 &ConfirmCodeAction {
1515 item_ix: Some(item_ix),
1516 },
1517 cx,
1518 ) {
1519 task.detach_and_log_err(cx)
1520 }
1521 }),
1522 )
1523 // TASK: It would be good to make lsp_action.title a SharedString to avoid allocating here.
1524 .child(SharedString::from(action.lsp_action.title.clone()))
1525 })
1526 .when_some(action.as_task(), |this, task| {
1527 this.on_mouse_down(
1528 MouseButton::Left,
1529 cx.listener(move |editor, _, cx| {
1530 cx.stop_propagation();
1531 if let Some(task) = editor.confirm_code_action(
1532 &ConfirmCodeAction {
1533 item_ix: Some(item_ix),
1534 },
1535 cx,
1536 ) {
1537 task.detach_and_log_err(cx)
1538 }
1539 }),
1540 )
1541 .child(SharedString::from(task.resolved_label.clone()))
1542 })
1543 })
1544 .collect()
1545 },
1546 )
1547 .elevation_1(cx)
1548 .p_1()
1549 .max_h(max_height)
1550 .occlude()
1551 .track_scroll(self.scroll_handle.clone())
1552 .with_width_from_item(
1553 self.actions
1554 .iter()
1555 .enumerate()
1556 .max_by_key(|(_, action)| match action {
1557 CodeActionsItem::Task(_, task) => task.resolved_label.chars().count(),
1558 CodeActionsItem::CodeAction(action) => action.lsp_action.title.chars().count(),
1559 })
1560 .map(|(ix, _)| ix),
1561 )
1562 .with_sizing_behavior(ListSizingBehavior::Infer)
1563 .into_any_element();
1564
1565 let cursor_position = if let Some(row) = self.deployed_from_indicator {
1566 ContextMenuOrigin::GutterIndicator(row)
1567 } else {
1568 ContextMenuOrigin::EditorPoint(cursor_position)
1569 };
1570
1571 (cursor_position, element)
1572 }
1573}
1574
1575#[derive(Debug)]
1576struct ActiveDiagnosticGroup {
1577 primary_range: Range<Anchor>,
1578 primary_message: String,
1579 group_id: usize,
1580 blocks: HashMap<CustomBlockId, Diagnostic>,
1581 is_valid: bool,
1582}
1583
1584#[derive(Serialize, Deserialize, Clone, Debug)]
1585pub struct ClipboardSelection {
1586 pub len: usize,
1587 pub is_entire_line: bool,
1588 pub first_line_indent: u32,
1589}
1590
1591#[derive(Debug)]
1592pub(crate) struct NavigationData {
1593 cursor_anchor: Anchor,
1594 cursor_position: Point,
1595 scroll_anchor: ScrollAnchor,
1596 scroll_top_row: u32,
1597}
1598
1599#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1600enum GotoDefinitionKind {
1601 Symbol,
1602 Declaration,
1603 Type,
1604 Implementation,
1605}
1606
1607#[derive(Debug, Clone)]
1608enum InlayHintRefreshReason {
1609 Toggle(bool),
1610 SettingsChange(InlayHintSettings),
1611 NewLinesShown,
1612 BufferEdited(HashSet<Arc<Language>>),
1613 RefreshRequested,
1614 ExcerptsRemoved(Vec<ExcerptId>),
1615}
1616
1617impl InlayHintRefreshReason {
1618 fn description(&self) -> &'static str {
1619 match self {
1620 Self::Toggle(_) => "toggle",
1621 Self::SettingsChange(_) => "settings change",
1622 Self::NewLinesShown => "new lines shown",
1623 Self::BufferEdited(_) => "buffer edited",
1624 Self::RefreshRequested => "refresh requested",
1625 Self::ExcerptsRemoved(_) => "excerpts removed",
1626 }
1627 }
1628}
1629
1630pub(crate) struct FocusedBlock {
1631 id: BlockId,
1632 focus_handle: WeakFocusHandle,
1633}
1634
1635impl Editor {
1636 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1637 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1638 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1639 Self::new(
1640 EditorMode::SingleLine { auto_width: false },
1641 buffer,
1642 None,
1643 false,
1644 cx,
1645 )
1646 }
1647
1648 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1649 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1650 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1651 Self::new(EditorMode::Full, buffer, None, false, cx)
1652 }
1653
1654 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1655 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1656 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1657 Self::new(
1658 EditorMode::SingleLine { auto_width: true },
1659 buffer,
1660 None,
1661 false,
1662 cx,
1663 )
1664 }
1665
1666 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1667 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1668 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1669 Self::new(
1670 EditorMode::AutoHeight { max_lines },
1671 buffer,
1672 None,
1673 false,
1674 cx,
1675 )
1676 }
1677
1678 pub fn for_buffer(
1679 buffer: Model<Buffer>,
1680 project: Option<Model<Project>>,
1681 cx: &mut ViewContext<Self>,
1682 ) -> Self {
1683 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1684 Self::new(EditorMode::Full, buffer, project, false, cx)
1685 }
1686
1687 pub fn for_multibuffer(
1688 buffer: Model<MultiBuffer>,
1689 project: Option<Model<Project>>,
1690 show_excerpt_controls: bool,
1691 cx: &mut ViewContext<Self>,
1692 ) -> Self {
1693 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1694 }
1695
1696 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1697 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1698 let mut clone = Self::new(
1699 self.mode,
1700 self.buffer.clone(),
1701 self.project.clone(),
1702 show_excerpt_controls,
1703 cx,
1704 );
1705 self.display_map.update(cx, |display_map, cx| {
1706 let snapshot = display_map.snapshot(cx);
1707 clone.display_map.update(cx, |display_map, cx| {
1708 display_map.set_state(&snapshot, cx);
1709 });
1710 });
1711 clone.selections.clone_state(&self.selections);
1712 clone.scroll_manager.clone_state(&self.scroll_manager);
1713 clone.searchable = self.searchable;
1714 clone
1715 }
1716
1717 pub fn new(
1718 mode: EditorMode,
1719 buffer: Model<MultiBuffer>,
1720 project: Option<Model<Project>>,
1721 show_excerpt_controls: bool,
1722 cx: &mut ViewContext<Self>,
1723 ) -> Self {
1724 let style = cx.text_style();
1725 let font_size = style.font_size.to_pixels(cx.rem_size());
1726 let editor = cx.view().downgrade();
1727 let fold_placeholder = FoldPlaceholder {
1728 constrain_width: true,
1729 render: Arc::new(move |fold_id, fold_range, cx| {
1730 let editor = editor.clone();
1731 div()
1732 .id(fold_id)
1733 .bg(cx.theme().colors().ghost_element_background)
1734 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1735 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1736 .rounded_sm()
1737 .size_full()
1738 .cursor_pointer()
1739 .child("⋯")
1740 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1741 .on_click(move |_, cx| {
1742 editor
1743 .update(cx, |editor, cx| {
1744 editor.unfold_ranges(
1745 [fold_range.start..fold_range.end],
1746 true,
1747 false,
1748 cx,
1749 );
1750 cx.stop_propagation();
1751 })
1752 .ok();
1753 })
1754 .into_any()
1755 }),
1756 merge_adjacent: true,
1757 };
1758 let file_header_size = if show_excerpt_controls { 3 } else { 2 };
1759 let display_map = cx.new_model(|cx| {
1760 DisplayMap::new(
1761 buffer.clone(),
1762 style.font(),
1763 font_size,
1764 None,
1765 show_excerpt_controls,
1766 file_header_size,
1767 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1768 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1769 fold_placeholder,
1770 cx,
1771 )
1772 });
1773
1774 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1775
1776 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1777
1778 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1779 .then(|| language_settings::SoftWrap::PreferLine);
1780
1781 let mut project_subscriptions = Vec::new();
1782 if mode == EditorMode::Full {
1783 if let Some(project) = project.as_ref() {
1784 if buffer.read(cx).is_singleton() {
1785 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1786 cx.emit(EditorEvent::TitleChanged);
1787 }));
1788 }
1789 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1790 if let project::Event::RefreshInlayHints = event {
1791 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1792 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1793 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1794 let focus_handle = editor.focus_handle(cx);
1795 if focus_handle.is_focused(cx) {
1796 let snapshot = buffer.read(cx).snapshot();
1797 for (range, snippet) in snippet_edits {
1798 let editor_range =
1799 language::range_from_lsp(*range).to_offset(&snapshot);
1800 editor
1801 .insert_snippet(&[editor_range], snippet.clone(), cx)
1802 .ok();
1803 }
1804 }
1805 }
1806 }
1807 }));
1808 let task_inventory = project.read(cx).task_inventory().clone();
1809 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1810 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1811 }));
1812 }
1813 }
1814
1815 let inlay_hint_settings = inlay_hint_settings(
1816 selections.newest_anchor().head(),
1817 &buffer.read(cx).snapshot(cx),
1818 cx,
1819 );
1820 let focus_handle = cx.focus_handle();
1821 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1822 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1823 .detach();
1824 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1825 .detach();
1826 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1827
1828 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1829 Some(false)
1830 } else {
1831 None
1832 };
1833
1834 let mut this = Self {
1835 focus_handle,
1836 show_cursor_when_unfocused: false,
1837 last_focused_descendant: None,
1838 buffer: buffer.clone(),
1839 display_map: display_map.clone(),
1840 selections,
1841 scroll_manager: ScrollManager::new(cx),
1842 columnar_selection_tail: None,
1843 add_selections_state: None,
1844 select_next_state: None,
1845 select_prev_state: None,
1846 selection_history: Default::default(),
1847 autoclose_regions: Default::default(),
1848 snippet_stack: Default::default(),
1849 select_larger_syntax_node_stack: Vec::new(),
1850 ime_transaction: Default::default(),
1851 active_diagnostics: None,
1852 soft_wrap_mode_override,
1853 completion_provider: project.clone().map(|project| Box::new(project) as _),
1854 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1855 project,
1856 blink_manager: blink_manager.clone(),
1857 show_local_selections: true,
1858 mode,
1859 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1860 show_gutter: mode == EditorMode::Full,
1861 show_line_numbers: None,
1862 use_relative_line_numbers: None,
1863 show_git_diff_gutter: None,
1864 show_code_actions: None,
1865 show_runnables: None,
1866 show_wrap_guides: None,
1867 show_indent_guides,
1868 placeholder_text: None,
1869 highlight_order: 0,
1870 highlighted_rows: HashMap::default(),
1871 background_highlights: Default::default(),
1872 gutter_highlights: TreeMap::default(),
1873 scrollbar_marker_state: ScrollbarMarkerState::default(),
1874 active_indent_guides_state: ActiveIndentGuidesState::default(),
1875 nav_history: None,
1876 context_menu: RwLock::new(None),
1877 mouse_context_menu: None,
1878 completion_tasks: Default::default(),
1879 signature_help_state: SignatureHelpState::default(),
1880 auto_signature_help: None,
1881 find_all_references_task_sources: Vec::new(),
1882 next_completion_id: 0,
1883 completion_documentation_pre_resolve_debounce: DebouncedDelay::new(),
1884 next_inlay_id: 0,
1885 available_code_actions: Default::default(),
1886 code_actions_task: Default::default(),
1887 document_highlights_task: Default::default(),
1888 linked_editing_range_task: Default::default(),
1889 pending_rename: Default::default(),
1890 searchable: true,
1891 cursor_shape: Default::default(),
1892 current_line_highlight: None,
1893 autoindent_mode: Some(AutoindentMode::EachLine),
1894 collapse_matches: false,
1895 workspace: None,
1896 input_enabled: true,
1897 use_modal_editing: mode == EditorMode::Full,
1898 read_only: false,
1899 use_autoclose: true,
1900 use_auto_surround: true,
1901 auto_replace_emoji_shortcode: false,
1902 leader_peer_id: None,
1903 remote_id: None,
1904 hover_state: Default::default(),
1905 hovered_link_state: Default::default(),
1906 inline_completion_provider: None,
1907 active_inline_completion: None,
1908 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1909 expanded_hunks: ExpandedHunks::default(),
1910 gutter_hovered: false,
1911 pixel_position_of_newest_cursor: None,
1912 last_bounds: None,
1913 expect_bounds_change: None,
1914 gutter_dimensions: GutterDimensions::default(),
1915 style: None,
1916 show_cursor_names: false,
1917 hovered_cursors: Default::default(),
1918 next_editor_action_id: EditorActionId::default(),
1919 editor_actions: Rc::default(),
1920 show_inline_completions_override: None,
1921 enable_inline_completions: true,
1922 custom_context_menu: None,
1923 show_git_blame_gutter: false,
1924 show_git_blame_inline: false,
1925 show_selection_menu: None,
1926 show_git_blame_inline_delay_task: None,
1927 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1928 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1929 .session
1930 .restore_unsaved_buffers,
1931 blame: None,
1932 blame_subscription: None,
1933 file_header_size,
1934 tasks: Default::default(),
1935 _subscriptions: vec![
1936 cx.observe(&buffer, Self::on_buffer_changed),
1937 cx.subscribe(&buffer, Self::on_buffer_event),
1938 cx.observe(&display_map, Self::on_display_map_changed),
1939 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1940 cx.observe_global::<SettingsStore>(Self::settings_changed),
1941 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1942 cx.observe_window_activation(|editor, cx| {
1943 let active = cx.is_window_active();
1944 editor.blink_manager.update(cx, |blink_manager, cx| {
1945 if active {
1946 blink_manager.enable(cx);
1947 } else {
1948 blink_manager.disable(cx);
1949 }
1950 });
1951 }),
1952 ],
1953 tasks_update_task: None,
1954 linked_edit_ranges: Default::default(),
1955 previous_search_ranges: None,
1956 breadcrumb_header: None,
1957 focused_block: None,
1958 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1959 addons: HashMap::default(),
1960 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1961 };
1962 this.tasks_update_task = Some(this.refresh_runnables(cx));
1963 this._subscriptions.extend(project_subscriptions);
1964
1965 this.end_selection(cx);
1966 this.scroll_manager.show_scrollbar(cx);
1967
1968 if mode == EditorMode::Full {
1969 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1970 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1971
1972 if this.git_blame_inline_enabled {
1973 this.git_blame_inline_enabled = true;
1974 this.start_git_blame_inline(false, cx);
1975 }
1976 }
1977
1978 this.report_editor_event("open", None, cx);
1979 this
1980 }
1981
1982 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1983 self.mouse_context_menu
1984 .as_ref()
1985 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1986 }
1987
1988 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1989 let mut key_context = KeyContext::new_with_defaults();
1990 key_context.add("Editor");
1991 let mode = match self.mode {
1992 EditorMode::SingleLine { .. } => "single_line",
1993 EditorMode::AutoHeight { .. } => "auto_height",
1994 EditorMode::Full => "full",
1995 };
1996
1997 if EditorSettings::jupyter_enabled(cx) {
1998 key_context.add("jupyter");
1999 }
2000
2001 key_context.set("mode", mode);
2002 if self.pending_rename.is_some() {
2003 key_context.add("renaming");
2004 }
2005 if self.context_menu_visible() {
2006 match self.context_menu.read().as_ref() {
2007 Some(ContextMenu::Completions(_)) => {
2008 key_context.add("menu");
2009 key_context.add("showing_completions")
2010 }
2011 Some(ContextMenu::CodeActions(_)) => {
2012 key_context.add("menu");
2013 key_context.add("showing_code_actions")
2014 }
2015 None => {}
2016 }
2017 }
2018
2019 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2020 if !self.focus_handle(cx).contains_focused(cx)
2021 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
2022 {
2023 for addon in self.addons.values() {
2024 addon.extend_key_context(&mut key_context, cx)
2025 }
2026 }
2027
2028 if let Some(extension) = self
2029 .buffer
2030 .read(cx)
2031 .as_singleton()
2032 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
2033 {
2034 key_context.set("extension", extension.to_string());
2035 }
2036
2037 if self.has_active_inline_completion(cx) {
2038 key_context.add("copilot_suggestion");
2039 key_context.add("inline_completion");
2040 }
2041
2042 key_context
2043 }
2044
2045 pub fn new_file(
2046 workspace: &mut Workspace,
2047 _: &workspace::NewFile,
2048 cx: &mut ViewContext<Workspace>,
2049 ) {
2050 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
2051 "Failed to create buffer",
2052 cx,
2053 |e, _| match e.error_code() {
2054 ErrorCode::RemoteUpgradeRequired => Some(format!(
2055 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2056 e.error_tag("required").unwrap_or("the latest version")
2057 )),
2058 _ => None,
2059 },
2060 );
2061 }
2062
2063 pub fn new_in_workspace(
2064 workspace: &mut Workspace,
2065 cx: &mut ViewContext<Workspace>,
2066 ) -> Task<Result<View<Editor>>> {
2067 let project = workspace.project().clone();
2068 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2069
2070 cx.spawn(|workspace, mut cx| async move {
2071 let buffer = create.await?;
2072 workspace.update(&mut cx, |workspace, cx| {
2073 let editor =
2074 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
2075 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
2076 editor
2077 })
2078 })
2079 }
2080
2081 fn new_file_vertical(
2082 workspace: &mut Workspace,
2083 _: &workspace::NewFileSplitVertical,
2084 cx: &mut ViewContext<Workspace>,
2085 ) {
2086 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
2087 }
2088
2089 fn new_file_horizontal(
2090 workspace: &mut Workspace,
2091 _: &workspace::NewFileSplitHorizontal,
2092 cx: &mut ViewContext<Workspace>,
2093 ) {
2094 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
2095 }
2096
2097 fn new_file_in_direction(
2098 workspace: &mut Workspace,
2099 direction: SplitDirection,
2100 cx: &mut ViewContext<Workspace>,
2101 ) {
2102 let project = workspace.project().clone();
2103 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2104
2105 cx.spawn(|workspace, mut cx| async move {
2106 let buffer = create.await?;
2107 workspace.update(&mut cx, move |workspace, cx| {
2108 workspace.split_item(
2109 direction,
2110 Box::new(
2111 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
2112 ),
2113 cx,
2114 )
2115 })?;
2116 anyhow::Ok(())
2117 })
2118 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
2119 ErrorCode::RemoteUpgradeRequired => Some(format!(
2120 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2121 e.error_tag("required").unwrap_or("the latest version")
2122 )),
2123 _ => None,
2124 });
2125 }
2126
2127 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
2128 self.buffer.read(cx).replica_id()
2129 }
2130
2131 pub fn leader_peer_id(&self) -> Option<PeerId> {
2132 self.leader_peer_id
2133 }
2134
2135 pub fn buffer(&self) -> &Model<MultiBuffer> {
2136 &self.buffer
2137 }
2138
2139 pub fn workspace(&self) -> Option<View<Workspace>> {
2140 self.workspace.as_ref()?.0.upgrade()
2141 }
2142
2143 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
2144 self.buffer().read(cx).title(cx)
2145 }
2146
2147 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
2148 EditorSnapshot {
2149 mode: self.mode,
2150 show_gutter: self.show_gutter,
2151 show_line_numbers: self.show_line_numbers,
2152 show_git_diff_gutter: self.show_git_diff_gutter,
2153 show_code_actions: self.show_code_actions,
2154 show_runnables: self.show_runnables,
2155 render_git_blame_gutter: self.render_git_blame_gutter(cx),
2156 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2157 scroll_anchor: self.scroll_manager.anchor(),
2158 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2159 placeholder_text: self.placeholder_text.clone(),
2160 is_focused: self.focus_handle.is_focused(cx),
2161 current_line_highlight: self
2162 .current_line_highlight
2163 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2164 gutter_hovered: self.gutter_hovered,
2165 }
2166 }
2167
2168 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
2169 self.buffer.read(cx).language_at(point, cx)
2170 }
2171
2172 pub fn file_at<T: ToOffset>(
2173 &self,
2174 point: T,
2175 cx: &AppContext,
2176 ) -> Option<Arc<dyn language::File>> {
2177 self.buffer.read(cx).read(cx).file_at(point).cloned()
2178 }
2179
2180 pub fn active_excerpt(
2181 &self,
2182 cx: &AppContext,
2183 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
2184 self.buffer
2185 .read(cx)
2186 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2187 }
2188
2189 pub fn mode(&self) -> EditorMode {
2190 self.mode
2191 }
2192
2193 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2194 self.collaboration_hub.as_deref()
2195 }
2196
2197 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2198 self.collaboration_hub = Some(hub);
2199 }
2200
2201 pub fn set_custom_context_menu(
2202 &mut self,
2203 f: impl 'static
2204 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
2205 ) {
2206 self.custom_context_menu = Some(Box::new(f))
2207 }
2208
2209 pub fn set_completion_provider(&mut self, provider: Box<dyn CompletionProvider>) {
2210 self.completion_provider = Some(provider);
2211 }
2212
2213 pub fn set_inline_completion_provider<T>(
2214 &mut self,
2215 provider: Option<Model<T>>,
2216 cx: &mut ViewContext<Self>,
2217 ) where
2218 T: InlineCompletionProvider,
2219 {
2220 self.inline_completion_provider =
2221 provider.map(|provider| RegisteredInlineCompletionProvider {
2222 _subscription: cx.observe(&provider, |this, _, cx| {
2223 if this.focus_handle.is_focused(cx) {
2224 this.update_visible_inline_completion(cx);
2225 }
2226 }),
2227 provider: Arc::new(provider),
2228 });
2229 self.refresh_inline_completion(false, false, cx);
2230 }
2231
2232 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
2233 self.placeholder_text.as_deref()
2234 }
2235
2236 pub fn set_placeholder_text(
2237 &mut self,
2238 placeholder_text: impl Into<Arc<str>>,
2239 cx: &mut ViewContext<Self>,
2240 ) {
2241 let placeholder_text = Some(placeholder_text.into());
2242 if self.placeholder_text != placeholder_text {
2243 self.placeholder_text = placeholder_text;
2244 cx.notify();
2245 }
2246 }
2247
2248 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
2249 self.cursor_shape = cursor_shape;
2250
2251 // Disrupt blink for immediate user feedback that the cursor shape has changed
2252 self.blink_manager.update(cx, BlinkManager::show_cursor);
2253
2254 cx.notify();
2255 }
2256
2257 pub fn set_current_line_highlight(
2258 &mut self,
2259 current_line_highlight: Option<CurrentLineHighlight>,
2260 ) {
2261 self.current_line_highlight = current_line_highlight;
2262 }
2263
2264 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2265 self.collapse_matches = collapse_matches;
2266 }
2267
2268 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2269 if self.collapse_matches {
2270 return range.start..range.start;
2271 }
2272 range.clone()
2273 }
2274
2275 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
2276 if self.display_map.read(cx).clip_at_line_ends != clip {
2277 self.display_map
2278 .update(cx, |map, _| map.clip_at_line_ends = clip);
2279 }
2280 }
2281
2282 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2283 self.input_enabled = input_enabled;
2284 }
2285
2286 pub fn set_inline_completions_enabled(&mut self, enabled: bool) {
2287 self.enable_inline_completions = enabled;
2288 }
2289
2290 pub fn set_autoindent(&mut self, autoindent: bool) {
2291 if autoindent {
2292 self.autoindent_mode = Some(AutoindentMode::EachLine);
2293 } else {
2294 self.autoindent_mode = None;
2295 }
2296 }
2297
2298 pub fn read_only(&self, cx: &AppContext) -> bool {
2299 self.read_only || self.buffer.read(cx).read_only()
2300 }
2301
2302 pub fn set_read_only(&mut self, read_only: bool) {
2303 self.read_only = read_only;
2304 }
2305
2306 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2307 self.use_autoclose = autoclose;
2308 }
2309
2310 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2311 self.use_auto_surround = auto_surround;
2312 }
2313
2314 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2315 self.auto_replace_emoji_shortcode = auto_replace;
2316 }
2317
2318 pub fn toggle_inline_completions(
2319 &mut self,
2320 _: &ToggleInlineCompletions,
2321 cx: &mut ViewContext<Self>,
2322 ) {
2323 if self.show_inline_completions_override.is_some() {
2324 self.set_show_inline_completions(None, cx);
2325 } else {
2326 let cursor = self.selections.newest_anchor().head();
2327 if let Some((buffer, cursor_buffer_position)) =
2328 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
2329 {
2330 let show_inline_completions =
2331 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
2332 self.set_show_inline_completions(Some(show_inline_completions), cx);
2333 }
2334 }
2335 }
2336
2337 pub fn set_show_inline_completions(
2338 &mut self,
2339 show_inline_completions: Option<bool>,
2340 cx: &mut ViewContext<Self>,
2341 ) {
2342 self.show_inline_completions_override = show_inline_completions;
2343 self.refresh_inline_completion(false, true, cx);
2344 }
2345
2346 fn should_show_inline_completions(
2347 &self,
2348 buffer: &Model<Buffer>,
2349 buffer_position: language::Anchor,
2350 cx: &AppContext,
2351 ) -> bool {
2352 if let Some(provider) = self.inline_completion_provider() {
2353 if let Some(show_inline_completions) = self.show_inline_completions_override {
2354 show_inline_completions
2355 } else {
2356 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
2357 }
2358 } else {
2359 false
2360 }
2361 }
2362
2363 pub fn set_use_modal_editing(&mut self, to: bool) {
2364 self.use_modal_editing = to;
2365 }
2366
2367 pub fn use_modal_editing(&self) -> bool {
2368 self.use_modal_editing
2369 }
2370
2371 fn selections_did_change(
2372 &mut self,
2373 local: bool,
2374 old_cursor_position: &Anchor,
2375 show_completions: bool,
2376 cx: &mut ViewContext<Self>,
2377 ) {
2378 cx.invalidate_character_coordinates();
2379
2380 // Copy selections to primary selection buffer
2381 #[cfg(target_os = "linux")]
2382 if local {
2383 let selections = self.selections.all::<usize>(cx);
2384 let buffer_handle = self.buffer.read(cx).read(cx);
2385
2386 let mut text = String::new();
2387 for (index, selection) in selections.iter().enumerate() {
2388 let text_for_selection = buffer_handle
2389 .text_for_range(selection.start..selection.end)
2390 .collect::<String>();
2391
2392 text.push_str(&text_for_selection);
2393 if index != selections.len() - 1 {
2394 text.push('\n');
2395 }
2396 }
2397
2398 if !text.is_empty() {
2399 cx.write_to_primary(ClipboardItem::new_string(text));
2400 }
2401 }
2402
2403 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
2404 self.buffer.update(cx, |buffer, cx| {
2405 buffer.set_active_selections(
2406 &self.selections.disjoint_anchors(),
2407 self.selections.line_mode,
2408 self.cursor_shape,
2409 cx,
2410 )
2411 });
2412 }
2413 let display_map = self
2414 .display_map
2415 .update(cx, |display_map, cx| display_map.snapshot(cx));
2416 let buffer = &display_map.buffer_snapshot;
2417 self.add_selections_state = None;
2418 self.select_next_state = None;
2419 self.select_prev_state = None;
2420 self.select_larger_syntax_node_stack.clear();
2421 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2422 self.snippet_stack
2423 .invalidate(&self.selections.disjoint_anchors(), buffer);
2424 self.take_rename(false, cx);
2425
2426 let new_cursor_position = self.selections.newest_anchor().head();
2427
2428 self.push_to_nav_history(
2429 *old_cursor_position,
2430 Some(new_cursor_position.to_point(buffer)),
2431 cx,
2432 );
2433
2434 if local {
2435 let new_cursor_position = self.selections.newest_anchor().head();
2436 let mut context_menu = self.context_menu.write();
2437 let completion_menu = match context_menu.as_ref() {
2438 Some(ContextMenu::Completions(menu)) => Some(menu),
2439
2440 _ => {
2441 *context_menu = None;
2442 None
2443 }
2444 };
2445
2446 if let Some(completion_menu) = completion_menu {
2447 let cursor_position = new_cursor_position.to_offset(buffer);
2448 let (word_range, kind) =
2449 buffer.surrounding_word(completion_menu.initial_position, true);
2450 if kind == Some(CharKind::Word)
2451 && word_range.to_inclusive().contains(&cursor_position)
2452 {
2453 let mut completion_menu = completion_menu.clone();
2454 drop(context_menu);
2455
2456 let query = Self::completion_query(buffer, cursor_position);
2457 cx.spawn(move |this, mut cx| async move {
2458 completion_menu
2459 .filter(query.as_deref(), cx.background_executor().clone())
2460 .await;
2461
2462 this.update(&mut cx, |this, cx| {
2463 let mut context_menu = this.context_menu.write();
2464 let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else {
2465 return;
2466 };
2467
2468 if menu.id > completion_menu.id {
2469 return;
2470 }
2471
2472 *context_menu = Some(ContextMenu::Completions(completion_menu));
2473 drop(context_menu);
2474 cx.notify();
2475 })
2476 })
2477 .detach();
2478
2479 if show_completions {
2480 self.show_completions(&ShowCompletions { trigger: None }, cx);
2481 }
2482 } else {
2483 drop(context_menu);
2484 self.hide_context_menu(cx);
2485 }
2486 } else {
2487 drop(context_menu);
2488 }
2489
2490 hide_hover(self, cx);
2491
2492 if old_cursor_position.to_display_point(&display_map).row()
2493 != new_cursor_position.to_display_point(&display_map).row()
2494 {
2495 self.available_code_actions.take();
2496 }
2497 self.refresh_code_actions(cx);
2498 self.refresh_document_highlights(cx);
2499 refresh_matching_bracket_highlights(self, cx);
2500 self.discard_inline_completion(false, cx);
2501 linked_editing_ranges::refresh_linked_ranges(self, cx);
2502 if self.git_blame_inline_enabled {
2503 self.start_inline_blame_timer(cx);
2504 }
2505 }
2506
2507 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2508 cx.emit(EditorEvent::SelectionsChanged { local });
2509
2510 if self.selections.disjoint_anchors().len() == 1 {
2511 cx.emit(SearchEvent::ActiveMatchChanged)
2512 }
2513 cx.notify();
2514 }
2515
2516 pub fn change_selections<R>(
2517 &mut self,
2518 autoscroll: Option<Autoscroll>,
2519 cx: &mut ViewContext<Self>,
2520 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2521 ) -> R {
2522 self.change_selections_inner(autoscroll, true, cx, change)
2523 }
2524
2525 pub fn change_selections_inner<R>(
2526 &mut self,
2527 autoscroll: Option<Autoscroll>,
2528 request_completions: bool,
2529 cx: &mut ViewContext<Self>,
2530 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2531 ) -> R {
2532 let old_cursor_position = self.selections.newest_anchor().head();
2533 self.push_to_selection_history();
2534
2535 let (changed, result) = self.selections.change_with(cx, change);
2536
2537 if changed {
2538 if let Some(autoscroll) = autoscroll {
2539 self.request_autoscroll(autoscroll, cx);
2540 }
2541 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2542
2543 if self.should_open_signature_help_automatically(
2544 &old_cursor_position,
2545 self.signature_help_state.backspace_pressed(),
2546 cx,
2547 ) {
2548 self.show_signature_help(&ShowSignatureHelp, cx);
2549 }
2550 self.signature_help_state.set_backspace_pressed(false);
2551 }
2552
2553 result
2554 }
2555
2556 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2557 where
2558 I: IntoIterator<Item = (Range<S>, T)>,
2559 S: ToOffset,
2560 T: Into<Arc<str>>,
2561 {
2562 if self.read_only(cx) {
2563 return;
2564 }
2565
2566 self.buffer
2567 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2568 }
2569
2570 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2571 where
2572 I: IntoIterator<Item = (Range<S>, T)>,
2573 S: ToOffset,
2574 T: Into<Arc<str>>,
2575 {
2576 if self.read_only(cx) {
2577 return;
2578 }
2579
2580 self.buffer.update(cx, |buffer, cx| {
2581 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2582 });
2583 }
2584
2585 pub fn edit_with_block_indent<I, S, T>(
2586 &mut self,
2587 edits: I,
2588 original_indent_columns: Vec<u32>,
2589 cx: &mut ViewContext<Self>,
2590 ) where
2591 I: IntoIterator<Item = (Range<S>, T)>,
2592 S: ToOffset,
2593 T: Into<Arc<str>>,
2594 {
2595 if self.read_only(cx) {
2596 return;
2597 }
2598
2599 self.buffer.update(cx, |buffer, cx| {
2600 buffer.edit(
2601 edits,
2602 Some(AutoindentMode::Block {
2603 original_indent_columns,
2604 }),
2605 cx,
2606 )
2607 });
2608 }
2609
2610 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2611 self.hide_context_menu(cx);
2612
2613 match phase {
2614 SelectPhase::Begin {
2615 position,
2616 add,
2617 click_count,
2618 } => self.begin_selection(position, add, click_count, cx),
2619 SelectPhase::BeginColumnar {
2620 position,
2621 goal_column,
2622 reset,
2623 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2624 SelectPhase::Extend {
2625 position,
2626 click_count,
2627 } => self.extend_selection(position, click_count, cx),
2628 SelectPhase::Update {
2629 position,
2630 goal_column,
2631 scroll_delta,
2632 } => self.update_selection(position, goal_column, scroll_delta, cx),
2633 SelectPhase::End => self.end_selection(cx),
2634 }
2635 }
2636
2637 fn extend_selection(
2638 &mut self,
2639 position: DisplayPoint,
2640 click_count: usize,
2641 cx: &mut ViewContext<Self>,
2642 ) {
2643 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2644 let tail = self.selections.newest::<usize>(cx).tail();
2645 self.begin_selection(position, false, click_count, cx);
2646
2647 let position = position.to_offset(&display_map, Bias::Left);
2648 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2649
2650 let mut pending_selection = self
2651 .selections
2652 .pending_anchor()
2653 .expect("extend_selection not called with pending selection");
2654 if position >= tail {
2655 pending_selection.start = tail_anchor;
2656 } else {
2657 pending_selection.end = tail_anchor;
2658 pending_selection.reversed = true;
2659 }
2660
2661 let mut pending_mode = self.selections.pending_mode().unwrap();
2662 match &mut pending_mode {
2663 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2664 _ => {}
2665 }
2666
2667 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2668 s.set_pending(pending_selection, pending_mode)
2669 });
2670 }
2671
2672 fn begin_selection(
2673 &mut self,
2674 position: DisplayPoint,
2675 add: bool,
2676 click_count: usize,
2677 cx: &mut ViewContext<Self>,
2678 ) {
2679 if !self.focus_handle.is_focused(cx) {
2680 self.last_focused_descendant = None;
2681 cx.focus(&self.focus_handle);
2682 }
2683
2684 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2685 let buffer = &display_map.buffer_snapshot;
2686 let newest_selection = self.selections.newest_anchor().clone();
2687 let position = display_map.clip_point(position, Bias::Left);
2688
2689 let start;
2690 let end;
2691 let mode;
2692 let auto_scroll;
2693 match click_count {
2694 1 => {
2695 start = buffer.anchor_before(position.to_point(&display_map));
2696 end = start;
2697 mode = SelectMode::Character;
2698 auto_scroll = true;
2699 }
2700 2 => {
2701 let range = movement::surrounding_word(&display_map, position);
2702 start = buffer.anchor_before(range.start.to_point(&display_map));
2703 end = buffer.anchor_before(range.end.to_point(&display_map));
2704 mode = SelectMode::Word(start..end);
2705 auto_scroll = true;
2706 }
2707 3 => {
2708 let position = display_map
2709 .clip_point(position, Bias::Left)
2710 .to_point(&display_map);
2711 let line_start = display_map.prev_line_boundary(position).0;
2712 let next_line_start = buffer.clip_point(
2713 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2714 Bias::Left,
2715 );
2716 start = buffer.anchor_before(line_start);
2717 end = buffer.anchor_before(next_line_start);
2718 mode = SelectMode::Line(start..end);
2719 auto_scroll = true;
2720 }
2721 _ => {
2722 start = buffer.anchor_before(0);
2723 end = buffer.anchor_before(buffer.len());
2724 mode = SelectMode::All;
2725 auto_scroll = false;
2726 }
2727 }
2728
2729 let point_to_delete: Option<usize> = {
2730 let selected_points: Vec<Selection<Point>> =
2731 self.selections.disjoint_in_range(start..end, cx);
2732
2733 if !add || click_count > 1 {
2734 None
2735 } else if !selected_points.is_empty() {
2736 Some(selected_points[0].id)
2737 } else {
2738 let clicked_point_already_selected =
2739 self.selections.disjoint.iter().find(|selection| {
2740 selection.start.to_point(buffer) == start.to_point(buffer)
2741 || selection.end.to_point(buffer) == end.to_point(buffer)
2742 });
2743
2744 clicked_point_already_selected.map(|selection| selection.id)
2745 }
2746 };
2747
2748 let selections_count = self.selections.count();
2749
2750 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
2751 if let Some(point_to_delete) = point_to_delete {
2752 s.delete(point_to_delete);
2753
2754 if selections_count == 1 {
2755 s.set_pending_anchor_range(start..end, mode);
2756 }
2757 } else {
2758 if !add {
2759 s.clear_disjoint();
2760 } else if click_count > 1 {
2761 s.delete(newest_selection.id)
2762 }
2763
2764 s.set_pending_anchor_range(start..end, mode);
2765 }
2766 });
2767 }
2768
2769 fn begin_columnar_selection(
2770 &mut self,
2771 position: DisplayPoint,
2772 goal_column: u32,
2773 reset: bool,
2774 cx: &mut ViewContext<Self>,
2775 ) {
2776 if !self.focus_handle.is_focused(cx) {
2777 self.last_focused_descendant = None;
2778 cx.focus(&self.focus_handle);
2779 }
2780
2781 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2782
2783 if reset {
2784 let pointer_position = display_map
2785 .buffer_snapshot
2786 .anchor_before(position.to_point(&display_map));
2787
2788 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2789 s.clear_disjoint();
2790 s.set_pending_anchor_range(
2791 pointer_position..pointer_position,
2792 SelectMode::Character,
2793 );
2794 });
2795 }
2796
2797 let tail = self.selections.newest::<Point>(cx).tail();
2798 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2799
2800 if !reset {
2801 self.select_columns(
2802 tail.to_display_point(&display_map),
2803 position,
2804 goal_column,
2805 &display_map,
2806 cx,
2807 );
2808 }
2809 }
2810
2811 fn update_selection(
2812 &mut self,
2813 position: DisplayPoint,
2814 goal_column: u32,
2815 scroll_delta: gpui::Point<f32>,
2816 cx: &mut ViewContext<Self>,
2817 ) {
2818 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2819
2820 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2821 let tail = tail.to_display_point(&display_map);
2822 self.select_columns(tail, position, goal_column, &display_map, cx);
2823 } else if let Some(mut pending) = self.selections.pending_anchor() {
2824 let buffer = self.buffer.read(cx).snapshot(cx);
2825 let head;
2826 let tail;
2827 let mode = self.selections.pending_mode().unwrap();
2828 match &mode {
2829 SelectMode::Character => {
2830 head = position.to_point(&display_map);
2831 tail = pending.tail().to_point(&buffer);
2832 }
2833 SelectMode::Word(original_range) => {
2834 let original_display_range = original_range.start.to_display_point(&display_map)
2835 ..original_range.end.to_display_point(&display_map);
2836 let original_buffer_range = original_display_range.start.to_point(&display_map)
2837 ..original_display_range.end.to_point(&display_map);
2838 if movement::is_inside_word(&display_map, position)
2839 || original_display_range.contains(&position)
2840 {
2841 let word_range = movement::surrounding_word(&display_map, position);
2842 if word_range.start < original_display_range.start {
2843 head = word_range.start.to_point(&display_map);
2844 } else {
2845 head = word_range.end.to_point(&display_map);
2846 }
2847 } else {
2848 head = position.to_point(&display_map);
2849 }
2850
2851 if head <= original_buffer_range.start {
2852 tail = original_buffer_range.end;
2853 } else {
2854 tail = original_buffer_range.start;
2855 }
2856 }
2857 SelectMode::Line(original_range) => {
2858 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2859
2860 let position = display_map
2861 .clip_point(position, Bias::Left)
2862 .to_point(&display_map);
2863 let line_start = display_map.prev_line_boundary(position).0;
2864 let next_line_start = buffer.clip_point(
2865 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2866 Bias::Left,
2867 );
2868
2869 if line_start < original_range.start {
2870 head = line_start
2871 } else {
2872 head = next_line_start
2873 }
2874
2875 if head <= original_range.start {
2876 tail = original_range.end;
2877 } else {
2878 tail = original_range.start;
2879 }
2880 }
2881 SelectMode::All => {
2882 return;
2883 }
2884 };
2885
2886 if head < tail {
2887 pending.start = buffer.anchor_before(head);
2888 pending.end = buffer.anchor_before(tail);
2889 pending.reversed = true;
2890 } else {
2891 pending.start = buffer.anchor_before(tail);
2892 pending.end = buffer.anchor_before(head);
2893 pending.reversed = false;
2894 }
2895
2896 self.change_selections(None, cx, |s| {
2897 s.set_pending(pending, mode);
2898 });
2899 } else {
2900 log::error!("update_selection dispatched with no pending selection");
2901 return;
2902 }
2903
2904 self.apply_scroll_delta(scroll_delta, cx);
2905 cx.notify();
2906 }
2907
2908 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2909 self.columnar_selection_tail.take();
2910 if self.selections.pending_anchor().is_some() {
2911 let selections = self.selections.all::<usize>(cx);
2912 self.change_selections(None, cx, |s| {
2913 s.select(selections);
2914 s.clear_pending();
2915 });
2916 }
2917 }
2918
2919 fn select_columns(
2920 &mut self,
2921 tail: DisplayPoint,
2922 head: DisplayPoint,
2923 goal_column: u32,
2924 display_map: &DisplaySnapshot,
2925 cx: &mut ViewContext<Self>,
2926 ) {
2927 let start_row = cmp::min(tail.row(), head.row());
2928 let end_row = cmp::max(tail.row(), head.row());
2929 let start_column = cmp::min(tail.column(), goal_column);
2930 let end_column = cmp::max(tail.column(), goal_column);
2931 let reversed = start_column < tail.column();
2932
2933 let selection_ranges = (start_row.0..=end_row.0)
2934 .map(DisplayRow)
2935 .filter_map(|row| {
2936 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2937 let start = display_map
2938 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2939 .to_point(display_map);
2940 let end = display_map
2941 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2942 .to_point(display_map);
2943 if reversed {
2944 Some(end..start)
2945 } else {
2946 Some(start..end)
2947 }
2948 } else {
2949 None
2950 }
2951 })
2952 .collect::<Vec<_>>();
2953
2954 self.change_selections(None, cx, |s| {
2955 s.select_ranges(selection_ranges);
2956 });
2957 cx.notify();
2958 }
2959
2960 pub fn has_pending_nonempty_selection(&self) -> bool {
2961 let pending_nonempty_selection = match self.selections.pending_anchor() {
2962 Some(Selection { start, end, .. }) => start != end,
2963 None => false,
2964 };
2965
2966 pending_nonempty_selection
2967 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2968 }
2969
2970 pub fn has_pending_selection(&self) -> bool {
2971 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2972 }
2973
2974 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2975 if self.clear_clicked_diff_hunks(cx) {
2976 cx.notify();
2977 return;
2978 }
2979 if self.dismiss_menus_and_popups(true, cx) {
2980 return;
2981 }
2982
2983 if self.mode == EditorMode::Full
2984 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
2985 {
2986 return;
2987 }
2988
2989 cx.propagate();
2990 }
2991
2992 pub fn dismiss_menus_and_popups(
2993 &mut self,
2994 should_report_inline_completion_event: bool,
2995 cx: &mut ViewContext<Self>,
2996 ) -> bool {
2997 if self.take_rename(false, cx).is_some() {
2998 return true;
2999 }
3000
3001 if hide_hover(self, cx) {
3002 return true;
3003 }
3004
3005 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3006 return true;
3007 }
3008
3009 if self.hide_context_menu(cx).is_some() {
3010 return true;
3011 }
3012
3013 if self.mouse_context_menu.take().is_some() {
3014 return true;
3015 }
3016
3017 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
3018 return true;
3019 }
3020
3021 if self.snippet_stack.pop().is_some() {
3022 return true;
3023 }
3024
3025 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
3026 self.dismiss_diagnostics(cx);
3027 return true;
3028 }
3029
3030 false
3031 }
3032
3033 fn linked_editing_ranges_for(
3034 &self,
3035 selection: Range<text::Anchor>,
3036 cx: &AppContext,
3037 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
3038 if self.linked_edit_ranges.is_empty() {
3039 return None;
3040 }
3041 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3042 selection.end.buffer_id.and_then(|end_buffer_id| {
3043 if selection.start.buffer_id != Some(end_buffer_id) {
3044 return None;
3045 }
3046 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3047 let snapshot = buffer.read(cx).snapshot();
3048 self.linked_edit_ranges
3049 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3050 .map(|ranges| (ranges, snapshot, buffer))
3051 })?;
3052 use text::ToOffset as TO;
3053 // find offset from the start of current range to current cursor position
3054 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3055
3056 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3057 let start_difference = start_offset - start_byte_offset;
3058 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3059 let end_difference = end_offset - start_byte_offset;
3060 // Current range has associated linked ranges.
3061 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3062 for range in linked_ranges.iter() {
3063 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3064 let end_offset = start_offset + end_difference;
3065 let start_offset = start_offset + start_difference;
3066 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3067 continue;
3068 }
3069 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
3070 if s.start.buffer_id != selection.start.buffer_id
3071 || s.end.buffer_id != selection.end.buffer_id
3072 {
3073 return false;
3074 }
3075 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3076 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3077 }) {
3078 continue;
3079 }
3080 let start = buffer_snapshot.anchor_after(start_offset);
3081 let end = buffer_snapshot.anchor_after(end_offset);
3082 linked_edits
3083 .entry(buffer.clone())
3084 .or_default()
3085 .push(start..end);
3086 }
3087 Some(linked_edits)
3088 }
3089
3090 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3091 let text: Arc<str> = text.into();
3092
3093 if self.read_only(cx) {
3094 return;
3095 }
3096
3097 let selections = self.selections.all_adjusted(cx);
3098 let mut bracket_inserted = false;
3099 let mut edits = Vec::new();
3100 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3101 let mut new_selections = Vec::with_capacity(selections.len());
3102 let mut new_autoclose_regions = Vec::new();
3103 let snapshot = self.buffer.read(cx).read(cx);
3104
3105 for (selection, autoclose_region) in
3106 self.selections_with_autoclose_regions(selections, &snapshot)
3107 {
3108 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3109 // Determine if the inserted text matches the opening or closing
3110 // bracket of any of this language's bracket pairs.
3111 let mut bracket_pair = None;
3112 let mut is_bracket_pair_start = false;
3113 let mut is_bracket_pair_end = false;
3114 if !text.is_empty() {
3115 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3116 // and they are removing the character that triggered IME popup.
3117 for (pair, enabled) in scope.brackets() {
3118 if !pair.close && !pair.surround {
3119 continue;
3120 }
3121
3122 if enabled && pair.start.ends_with(text.as_ref()) {
3123 bracket_pair = Some(pair.clone());
3124 is_bracket_pair_start = true;
3125 break;
3126 }
3127 if pair.end.as_str() == text.as_ref() {
3128 bracket_pair = Some(pair.clone());
3129 is_bracket_pair_end = true;
3130 break;
3131 }
3132 }
3133 }
3134
3135 if let Some(bracket_pair) = bracket_pair {
3136 let snapshot_settings = snapshot.settings_at(selection.start, cx);
3137 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3138 let auto_surround =
3139 self.use_auto_surround && snapshot_settings.use_auto_surround;
3140 if selection.is_empty() {
3141 if is_bracket_pair_start {
3142 let prefix_len = bracket_pair.start.len() - text.len();
3143
3144 // If the inserted text is a suffix of an opening bracket and the
3145 // selection is preceded by the rest of the opening bracket, then
3146 // insert the closing bracket.
3147 let following_text_allows_autoclose = snapshot
3148 .chars_at(selection.start)
3149 .next()
3150 .map_or(true, |c| scope.should_autoclose_before(c));
3151 let preceding_text_matches_prefix = prefix_len == 0
3152 || (selection.start.column >= (prefix_len as u32)
3153 && snapshot.contains_str_at(
3154 Point::new(
3155 selection.start.row,
3156 selection.start.column - (prefix_len as u32),
3157 ),
3158 &bracket_pair.start[..prefix_len],
3159 ));
3160
3161 if autoclose
3162 && bracket_pair.close
3163 && following_text_allows_autoclose
3164 && preceding_text_matches_prefix
3165 {
3166 let anchor = snapshot.anchor_before(selection.end);
3167 new_selections.push((selection.map(|_| anchor), text.len()));
3168 new_autoclose_regions.push((
3169 anchor,
3170 text.len(),
3171 selection.id,
3172 bracket_pair.clone(),
3173 ));
3174 edits.push((
3175 selection.range(),
3176 format!("{}{}", text, bracket_pair.end).into(),
3177 ));
3178 bracket_inserted = true;
3179 continue;
3180 }
3181 }
3182
3183 if let Some(region) = autoclose_region {
3184 // If the selection is followed by an auto-inserted closing bracket,
3185 // then don't insert that closing bracket again; just move the selection
3186 // past the closing bracket.
3187 let should_skip = selection.end == region.range.end.to_point(&snapshot)
3188 && text.as_ref() == region.pair.end.as_str();
3189 if should_skip {
3190 let anchor = snapshot.anchor_after(selection.end);
3191 new_selections
3192 .push((selection.map(|_| anchor), region.pair.end.len()));
3193 continue;
3194 }
3195 }
3196
3197 let always_treat_brackets_as_autoclosed = snapshot
3198 .settings_at(selection.start, cx)
3199 .always_treat_brackets_as_autoclosed;
3200 if always_treat_brackets_as_autoclosed
3201 && is_bracket_pair_end
3202 && snapshot.contains_str_at(selection.end, text.as_ref())
3203 {
3204 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
3205 // and the inserted text is a closing bracket and the selection is followed
3206 // by the closing bracket then move the selection past the closing bracket.
3207 let anchor = snapshot.anchor_after(selection.end);
3208 new_selections.push((selection.map(|_| anchor), text.len()));
3209 continue;
3210 }
3211 }
3212 // If an opening bracket is 1 character long and is typed while
3213 // text is selected, then surround that text with the bracket pair.
3214 else if auto_surround
3215 && bracket_pair.surround
3216 && is_bracket_pair_start
3217 && bracket_pair.start.chars().count() == 1
3218 {
3219 edits.push((selection.start..selection.start, text.clone()));
3220 edits.push((
3221 selection.end..selection.end,
3222 bracket_pair.end.as_str().into(),
3223 ));
3224 bracket_inserted = true;
3225 new_selections.push((
3226 Selection {
3227 id: selection.id,
3228 start: snapshot.anchor_after(selection.start),
3229 end: snapshot.anchor_before(selection.end),
3230 reversed: selection.reversed,
3231 goal: selection.goal,
3232 },
3233 0,
3234 ));
3235 continue;
3236 }
3237 }
3238 }
3239
3240 if self.auto_replace_emoji_shortcode
3241 && selection.is_empty()
3242 && text.as_ref().ends_with(':')
3243 {
3244 if let Some(possible_emoji_short_code) =
3245 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3246 {
3247 if !possible_emoji_short_code.is_empty() {
3248 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3249 let emoji_shortcode_start = Point::new(
3250 selection.start.row,
3251 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3252 );
3253
3254 // Remove shortcode from buffer
3255 edits.push((
3256 emoji_shortcode_start..selection.start,
3257 "".to_string().into(),
3258 ));
3259 new_selections.push((
3260 Selection {
3261 id: selection.id,
3262 start: snapshot.anchor_after(emoji_shortcode_start),
3263 end: snapshot.anchor_before(selection.start),
3264 reversed: selection.reversed,
3265 goal: selection.goal,
3266 },
3267 0,
3268 ));
3269
3270 // Insert emoji
3271 let selection_start_anchor = snapshot.anchor_after(selection.start);
3272 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3273 edits.push((selection.start..selection.end, emoji.to_string().into()));
3274
3275 continue;
3276 }
3277 }
3278 }
3279 }
3280
3281 // If not handling any auto-close operation, then just replace the selected
3282 // text with the given input and move the selection to the end of the
3283 // newly inserted text.
3284 let anchor = snapshot.anchor_after(selection.end);
3285 if !self.linked_edit_ranges.is_empty() {
3286 let start_anchor = snapshot.anchor_before(selection.start);
3287
3288 let is_word_char = text.chars().next().map_or(true, |char| {
3289 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3290 classifier.is_word(char)
3291 });
3292
3293 if is_word_char {
3294 if let Some(ranges) = self
3295 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3296 {
3297 for (buffer, edits) in ranges {
3298 linked_edits
3299 .entry(buffer.clone())
3300 .or_default()
3301 .extend(edits.into_iter().map(|range| (range, text.clone())));
3302 }
3303 }
3304 }
3305 }
3306
3307 new_selections.push((selection.map(|_| anchor), 0));
3308 edits.push((selection.start..selection.end, text.clone()));
3309 }
3310
3311 drop(snapshot);
3312
3313 self.transact(cx, |this, cx| {
3314 this.buffer.update(cx, |buffer, cx| {
3315 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3316 });
3317 for (buffer, edits) in linked_edits {
3318 buffer.update(cx, |buffer, cx| {
3319 let snapshot = buffer.snapshot();
3320 let edits = edits
3321 .into_iter()
3322 .map(|(range, text)| {
3323 use text::ToPoint as TP;
3324 let end_point = TP::to_point(&range.end, &snapshot);
3325 let start_point = TP::to_point(&range.start, &snapshot);
3326 (start_point..end_point, text)
3327 })
3328 .sorted_by_key(|(range, _)| range.start)
3329 .collect::<Vec<_>>();
3330 buffer.edit(edits, None, cx);
3331 })
3332 }
3333 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3334 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3335 let snapshot = this.buffer.read(cx).read(cx);
3336 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
3337 .zip(new_selection_deltas)
3338 .map(|(selection, delta)| Selection {
3339 id: selection.id,
3340 start: selection.start + delta,
3341 end: selection.end + delta,
3342 reversed: selection.reversed,
3343 goal: SelectionGoal::None,
3344 })
3345 .collect::<Vec<_>>();
3346
3347 let mut i = 0;
3348 for (position, delta, selection_id, pair) in new_autoclose_regions {
3349 let position = position.to_offset(&snapshot) + delta;
3350 let start = snapshot.anchor_before(position);
3351 let end = snapshot.anchor_after(position);
3352 while let Some(existing_state) = this.autoclose_regions.get(i) {
3353 match existing_state.range.start.cmp(&start, &snapshot) {
3354 Ordering::Less => i += 1,
3355 Ordering::Greater => break,
3356 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
3357 Ordering::Less => i += 1,
3358 Ordering::Equal => break,
3359 Ordering::Greater => break,
3360 },
3361 }
3362 }
3363 this.autoclose_regions.insert(
3364 i,
3365 AutocloseRegion {
3366 selection_id,
3367 range: start..end,
3368 pair,
3369 },
3370 );
3371 }
3372
3373 drop(snapshot);
3374 let had_active_inline_completion = this.has_active_inline_completion(cx);
3375 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
3376 s.select(new_selections)
3377 });
3378
3379 if !bracket_inserted && EditorSettings::get_global(cx).use_on_type_format {
3380 if let Some(on_type_format_task) =
3381 this.trigger_on_type_formatting(text.to_string(), cx)
3382 {
3383 on_type_format_task.detach_and_log_err(cx);
3384 }
3385 }
3386
3387 let editor_settings = EditorSettings::get_global(cx);
3388 if bracket_inserted
3389 && (editor_settings.auto_signature_help
3390 || editor_settings.show_signature_help_after_edits)
3391 {
3392 this.show_signature_help(&ShowSignatureHelp, cx);
3393 }
3394
3395 let trigger_in_words = !had_active_inline_completion;
3396 this.trigger_completion_on_input(&text, trigger_in_words, cx);
3397 linked_editing_ranges::refresh_linked_ranges(this, cx);
3398 this.refresh_inline_completion(true, false, cx);
3399 });
3400 }
3401
3402 fn find_possible_emoji_shortcode_at_position(
3403 snapshot: &MultiBufferSnapshot,
3404 position: Point,
3405 ) -> Option<String> {
3406 let mut chars = Vec::new();
3407 let mut found_colon = false;
3408 for char in snapshot.reversed_chars_at(position).take(100) {
3409 // Found a possible emoji shortcode in the middle of the buffer
3410 if found_colon {
3411 if char.is_whitespace() {
3412 chars.reverse();
3413 return Some(chars.iter().collect());
3414 }
3415 // If the previous character is not a whitespace, we are in the middle of a word
3416 // and we only want to complete the shortcode if the word is made up of other emojis
3417 let mut containing_word = String::new();
3418 for ch in snapshot
3419 .reversed_chars_at(position)
3420 .skip(chars.len() + 1)
3421 .take(100)
3422 {
3423 if ch.is_whitespace() {
3424 break;
3425 }
3426 containing_word.push(ch);
3427 }
3428 let containing_word = containing_word.chars().rev().collect::<String>();
3429 if util::word_consists_of_emojis(containing_word.as_str()) {
3430 chars.reverse();
3431 return Some(chars.iter().collect());
3432 }
3433 }
3434
3435 if char.is_whitespace() || !char.is_ascii() {
3436 return None;
3437 }
3438 if char == ':' {
3439 found_colon = true;
3440 } else {
3441 chars.push(char);
3442 }
3443 }
3444 // Found a possible emoji shortcode at the beginning of the buffer
3445 chars.reverse();
3446 Some(chars.iter().collect())
3447 }
3448
3449 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
3450 self.transact(cx, |this, cx| {
3451 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3452 let selections = this.selections.all::<usize>(cx);
3453 let multi_buffer = this.buffer.read(cx);
3454 let buffer = multi_buffer.snapshot(cx);
3455 selections
3456 .iter()
3457 .map(|selection| {
3458 let start_point = selection.start.to_point(&buffer);
3459 let mut indent =
3460 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3461 indent.len = cmp::min(indent.len, start_point.column);
3462 let start = selection.start;
3463 let end = selection.end;
3464 let selection_is_empty = start == end;
3465 let language_scope = buffer.language_scope_at(start);
3466 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3467 &language_scope
3468 {
3469 let leading_whitespace_len = buffer
3470 .reversed_chars_at(start)
3471 .take_while(|c| c.is_whitespace() && *c != '\n')
3472 .map(|c| c.len_utf8())
3473 .sum::<usize>();
3474
3475 let trailing_whitespace_len = buffer
3476 .chars_at(end)
3477 .take_while(|c| c.is_whitespace() && *c != '\n')
3478 .map(|c| c.len_utf8())
3479 .sum::<usize>();
3480
3481 let insert_extra_newline =
3482 language.brackets().any(|(pair, enabled)| {
3483 let pair_start = pair.start.trim_end();
3484 let pair_end = pair.end.trim_start();
3485
3486 enabled
3487 && pair.newline
3488 && buffer.contains_str_at(
3489 end + trailing_whitespace_len,
3490 pair_end,
3491 )
3492 && buffer.contains_str_at(
3493 (start - leading_whitespace_len)
3494 .saturating_sub(pair_start.len()),
3495 pair_start,
3496 )
3497 });
3498
3499 // Comment extension on newline is allowed only for cursor selections
3500 let comment_delimiter = maybe!({
3501 if !selection_is_empty {
3502 return None;
3503 }
3504
3505 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3506 return None;
3507 }
3508
3509 let delimiters = language.line_comment_prefixes();
3510 let max_len_of_delimiter =
3511 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3512 let (snapshot, range) =
3513 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3514
3515 let mut index_of_first_non_whitespace = 0;
3516 let comment_candidate = snapshot
3517 .chars_for_range(range)
3518 .skip_while(|c| {
3519 let should_skip = c.is_whitespace();
3520 if should_skip {
3521 index_of_first_non_whitespace += 1;
3522 }
3523 should_skip
3524 })
3525 .take(max_len_of_delimiter)
3526 .collect::<String>();
3527 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3528 comment_candidate.starts_with(comment_prefix.as_ref())
3529 })?;
3530 let cursor_is_placed_after_comment_marker =
3531 index_of_first_non_whitespace + comment_prefix.len()
3532 <= start_point.column as usize;
3533 if cursor_is_placed_after_comment_marker {
3534 Some(comment_prefix.clone())
3535 } else {
3536 None
3537 }
3538 });
3539 (comment_delimiter, insert_extra_newline)
3540 } else {
3541 (None, false)
3542 };
3543
3544 let capacity_for_delimiter = comment_delimiter
3545 .as_deref()
3546 .map(str::len)
3547 .unwrap_or_default();
3548 let mut new_text =
3549 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3550 new_text.push('\n');
3551 new_text.extend(indent.chars());
3552 if let Some(delimiter) = &comment_delimiter {
3553 new_text.push_str(delimiter);
3554 }
3555 if insert_extra_newline {
3556 new_text = new_text.repeat(2);
3557 }
3558
3559 let anchor = buffer.anchor_after(end);
3560 let new_selection = selection.map(|_| anchor);
3561 (
3562 (start..end, new_text),
3563 (insert_extra_newline, new_selection),
3564 )
3565 })
3566 .unzip()
3567 };
3568
3569 this.edit_with_autoindent(edits, cx);
3570 let buffer = this.buffer.read(cx).snapshot(cx);
3571 let new_selections = selection_fixup_info
3572 .into_iter()
3573 .map(|(extra_newline_inserted, new_selection)| {
3574 let mut cursor = new_selection.end.to_point(&buffer);
3575 if extra_newline_inserted {
3576 cursor.row -= 1;
3577 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3578 }
3579 new_selection.map(|_| cursor)
3580 })
3581 .collect();
3582
3583 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3584 this.refresh_inline_completion(true, false, cx);
3585 });
3586 }
3587
3588 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3589 let buffer = self.buffer.read(cx);
3590 let snapshot = buffer.snapshot(cx);
3591
3592 let mut edits = Vec::new();
3593 let mut rows = Vec::new();
3594
3595 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3596 let cursor = selection.head();
3597 let row = cursor.row;
3598
3599 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3600
3601 let newline = "\n".to_string();
3602 edits.push((start_of_line..start_of_line, newline));
3603
3604 rows.push(row + rows_inserted as u32);
3605 }
3606
3607 self.transact(cx, |editor, cx| {
3608 editor.edit(edits, cx);
3609
3610 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3611 let mut index = 0;
3612 s.move_cursors_with(|map, _, _| {
3613 let row = rows[index];
3614 index += 1;
3615
3616 let point = Point::new(row, 0);
3617 let boundary = map.next_line_boundary(point).1;
3618 let clipped = map.clip_point(boundary, Bias::Left);
3619
3620 (clipped, SelectionGoal::None)
3621 });
3622 });
3623
3624 let mut indent_edits = Vec::new();
3625 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3626 for row in rows {
3627 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3628 for (row, indent) in indents {
3629 if indent.len == 0 {
3630 continue;
3631 }
3632
3633 let text = match indent.kind {
3634 IndentKind::Space => " ".repeat(indent.len as usize),
3635 IndentKind::Tab => "\t".repeat(indent.len as usize),
3636 };
3637 let point = Point::new(row.0, 0);
3638 indent_edits.push((point..point, text));
3639 }
3640 }
3641 editor.edit(indent_edits, cx);
3642 });
3643 }
3644
3645 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3646 let buffer = self.buffer.read(cx);
3647 let snapshot = buffer.snapshot(cx);
3648
3649 let mut edits = Vec::new();
3650 let mut rows = Vec::new();
3651 let mut rows_inserted = 0;
3652
3653 for selection in self.selections.all_adjusted(cx) {
3654 let cursor = selection.head();
3655 let row = cursor.row;
3656
3657 let point = Point::new(row + 1, 0);
3658 let start_of_line = snapshot.clip_point(point, Bias::Left);
3659
3660 let newline = "\n".to_string();
3661 edits.push((start_of_line..start_of_line, newline));
3662
3663 rows_inserted += 1;
3664 rows.push(row + rows_inserted);
3665 }
3666
3667 self.transact(cx, |editor, cx| {
3668 editor.edit(edits, cx);
3669
3670 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3671 let mut index = 0;
3672 s.move_cursors_with(|map, _, _| {
3673 let row = rows[index];
3674 index += 1;
3675
3676 let point = Point::new(row, 0);
3677 let boundary = map.next_line_boundary(point).1;
3678 let clipped = map.clip_point(boundary, Bias::Left);
3679
3680 (clipped, SelectionGoal::None)
3681 });
3682 });
3683
3684 let mut indent_edits = Vec::new();
3685 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3686 for row in rows {
3687 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3688 for (row, indent) in indents {
3689 if indent.len == 0 {
3690 continue;
3691 }
3692
3693 let text = match indent.kind {
3694 IndentKind::Space => " ".repeat(indent.len as usize),
3695 IndentKind::Tab => "\t".repeat(indent.len as usize),
3696 };
3697 let point = Point::new(row.0, 0);
3698 indent_edits.push((point..point, text));
3699 }
3700 }
3701 editor.edit(indent_edits, cx);
3702 });
3703 }
3704
3705 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3706 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3707 original_indent_columns: Vec::new(),
3708 });
3709 self.insert_with_autoindent_mode(text, autoindent, cx);
3710 }
3711
3712 fn insert_with_autoindent_mode(
3713 &mut self,
3714 text: &str,
3715 autoindent_mode: Option<AutoindentMode>,
3716 cx: &mut ViewContext<Self>,
3717 ) {
3718 if self.read_only(cx) {
3719 return;
3720 }
3721
3722 let text: Arc<str> = text.into();
3723 self.transact(cx, |this, cx| {
3724 let old_selections = this.selections.all_adjusted(cx);
3725 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3726 let anchors = {
3727 let snapshot = buffer.read(cx);
3728 old_selections
3729 .iter()
3730 .map(|s| {
3731 let anchor = snapshot.anchor_after(s.head());
3732 s.map(|_| anchor)
3733 })
3734 .collect::<Vec<_>>()
3735 };
3736 buffer.edit(
3737 old_selections
3738 .iter()
3739 .map(|s| (s.start..s.end, text.clone())),
3740 autoindent_mode,
3741 cx,
3742 );
3743 anchors
3744 });
3745
3746 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3747 s.select_anchors(selection_anchors);
3748 })
3749 });
3750 }
3751
3752 fn trigger_completion_on_input(
3753 &mut self,
3754 text: &str,
3755 trigger_in_words: bool,
3756 cx: &mut ViewContext<Self>,
3757 ) {
3758 if self.is_completion_trigger(text, trigger_in_words, cx) {
3759 self.show_completions(
3760 &ShowCompletions {
3761 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3762 },
3763 cx,
3764 );
3765 } else {
3766 self.hide_context_menu(cx);
3767 }
3768 }
3769
3770 fn is_completion_trigger(
3771 &self,
3772 text: &str,
3773 trigger_in_words: bool,
3774 cx: &mut ViewContext<Self>,
3775 ) -> bool {
3776 let position = self.selections.newest_anchor().head();
3777 let multibuffer = self.buffer.read(cx);
3778 let Some(buffer) = position
3779 .buffer_id
3780 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3781 else {
3782 return false;
3783 };
3784
3785 if let Some(completion_provider) = &self.completion_provider {
3786 completion_provider.is_completion_trigger(
3787 &buffer,
3788 position.text_anchor,
3789 text,
3790 trigger_in_words,
3791 cx,
3792 )
3793 } else {
3794 false
3795 }
3796 }
3797
3798 /// If any empty selections is touching the start of its innermost containing autoclose
3799 /// region, expand it to select the brackets.
3800 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3801 let selections = self.selections.all::<usize>(cx);
3802 let buffer = self.buffer.read(cx).read(cx);
3803 let new_selections = self
3804 .selections_with_autoclose_regions(selections, &buffer)
3805 .map(|(mut selection, region)| {
3806 if !selection.is_empty() {
3807 return selection;
3808 }
3809
3810 if let Some(region) = region {
3811 let mut range = region.range.to_offset(&buffer);
3812 if selection.start == range.start && range.start >= region.pair.start.len() {
3813 range.start -= region.pair.start.len();
3814 if buffer.contains_str_at(range.start, ®ion.pair.start)
3815 && buffer.contains_str_at(range.end, ®ion.pair.end)
3816 {
3817 range.end += region.pair.end.len();
3818 selection.start = range.start;
3819 selection.end = range.end;
3820
3821 return selection;
3822 }
3823 }
3824 }
3825
3826 let always_treat_brackets_as_autoclosed = buffer
3827 .settings_at(selection.start, cx)
3828 .always_treat_brackets_as_autoclosed;
3829
3830 if !always_treat_brackets_as_autoclosed {
3831 return selection;
3832 }
3833
3834 if let Some(scope) = buffer.language_scope_at(selection.start) {
3835 for (pair, enabled) in scope.brackets() {
3836 if !enabled || !pair.close {
3837 continue;
3838 }
3839
3840 if buffer.contains_str_at(selection.start, &pair.end) {
3841 let pair_start_len = pair.start.len();
3842 if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
3843 {
3844 selection.start -= pair_start_len;
3845 selection.end += pair.end.len();
3846
3847 return selection;
3848 }
3849 }
3850 }
3851 }
3852
3853 selection
3854 })
3855 .collect();
3856
3857 drop(buffer);
3858 self.change_selections(None, cx, |selections| selections.select(new_selections));
3859 }
3860
3861 /// Iterate the given selections, and for each one, find the smallest surrounding
3862 /// autoclose region. This uses the ordering of the selections and the autoclose
3863 /// regions to avoid repeated comparisons.
3864 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3865 &'a self,
3866 selections: impl IntoIterator<Item = Selection<D>>,
3867 buffer: &'a MultiBufferSnapshot,
3868 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3869 let mut i = 0;
3870 let mut regions = self.autoclose_regions.as_slice();
3871 selections.into_iter().map(move |selection| {
3872 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3873
3874 let mut enclosing = None;
3875 while let Some(pair_state) = regions.get(i) {
3876 if pair_state.range.end.to_offset(buffer) < range.start {
3877 regions = ®ions[i + 1..];
3878 i = 0;
3879 } else if pair_state.range.start.to_offset(buffer) > range.end {
3880 break;
3881 } else {
3882 if pair_state.selection_id == selection.id {
3883 enclosing = Some(pair_state);
3884 }
3885 i += 1;
3886 }
3887 }
3888
3889 (selection.clone(), enclosing)
3890 })
3891 }
3892
3893 /// Remove any autoclose regions that no longer contain their selection.
3894 fn invalidate_autoclose_regions(
3895 &mut self,
3896 mut selections: &[Selection<Anchor>],
3897 buffer: &MultiBufferSnapshot,
3898 ) {
3899 self.autoclose_regions.retain(|state| {
3900 let mut i = 0;
3901 while let Some(selection) = selections.get(i) {
3902 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3903 selections = &selections[1..];
3904 continue;
3905 }
3906 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3907 break;
3908 }
3909 if selection.id == state.selection_id {
3910 return true;
3911 } else {
3912 i += 1;
3913 }
3914 }
3915 false
3916 });
3917 }
3918
3919 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3920 let offset = position.to_offset(buffer);
3921 let (word_range, kind) = buffer.surrounding_word(offset, true);
3922 if offset > word_range.start && kind == Some(CharKind::Word) {
3923 Some(
3924 buffer
3925 .text_for_range(word_range.start..offset)
3926 .collect::<String>(),
3927 )
3928 } else {
3929 None
3930 }
3931 }
3932
3933 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3934 self.refresh_inlay_hints(
3935 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3936 cx,
3937 );
3938 }
3939
3940 pub fn inlay_hints_enabled(&self) -> bool {
3941 self.inlay_hint_cache.enabled
3942 }
3943
3944 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3945 if self.project.is_none() || self.mode != EditorMode::Full {
3946 return;
3947 }
3948
3949 let reason_description = reason.description();
3950 let ignore_debounce = matches!(
3951 reason,
3952 InlayHintRefreshReason::SettingsChange(_)
3953 | InlayHintRefreshReason::Toggle(_)
3954 | InlayHintRefreshReason::ExcerptsRemoved(_)
3955 );
3956 let (invalidate_cache, required_languages) = match reason {
3957 InlayHintRefreshReason::Toggle(enabled) => {
3958 self.inlay_hint_cache.enabled = enabled;
3959 if enabled {
3960 (InvalidationStrategy::RefreshRequested, None)
3961 } else {
3962 self.inlay_hint_cache.clear();
3963 self.splice_inlays(
3964 self.visible_inlay_hints(cx)
3965 .iter()
3966 .map(|inlay| inlay.id)
3967 .collect(),
3968 Vec::new(),
3969 cx,
3970 );
3971 return;
3972 }
3973 }
3974 InlayHintRefreshReason::SettingsChange(new_settings) => {
3975 match self.inlay_hint_cache.update_settings(
3976 &self.buffer,
3977 new_settings,
3978 self.visible_inlay_hints(cx),
3979 cx,
3980 ) {
3981 ControlFlow::Break(Some(InlaySplice {
3982 to_remove,
3983 to_insert,
3984 })) => {
3985 self.splice_inlays(to_remove, to_insert, cx);
3986 return;
3987 }
3988 ControlFlow::Break(None) => return,
3989 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3990 }
3991 }
3992 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3993 if let Some(InlaySplice {
3994 to_remove,
3995 to_insert,
3996 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3997 {
3998 self.splice_inlays(to_remove, to_insert, cx);
3999 }
4000 return;
4001 }
4002 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
4003 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
4004 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
4005 }
4006 InlayHintRefreshReason::RefreshRequested => {
4007 (InvalidationStrategy::RefreshRequested, None)
4008 }
4009 };
4010
4011 if let Some(InlaySplice {
4012 to_remove,
4013 to_insert,
4014 }) = self.inlay_hint_cache.spawn_hint_refresh(
4015 reason_description,
4016 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
4017 invalidate_cache,
4018 ignore_debounce,
4019 cx,
4020 ) {
4021 self.splice_inlays(to_remove, to_insert, cx);
4022 }
4023 }
4024
4025 fn visible_inlay_hints(&self, cx: &ViewContext<'_, Editor>) -> Vec<Inlay> {
4026 self.display_map
4027 .read(cx)
4028 .current_inlays()
4029 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
4030 .cloned()
4031 .collect()
4032 }
4033
4034 pub fn excerpts_for_inlay_hints_query(
4035 &self,
4036 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
4037 cx: &mut ViewContext<Editor>,
4038 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
4039 let Some(project) = self.project.as_ref() else {
4040 return HashMap::default();
4041 };
4042 let project = project.read(cx);
4043 let multi_buffer = self.buffer().read(cx);
4044 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
4045 let multi_buffer_visible_start = self
4046 .scroll_manager
4047 .anchor()
4048 .anchor
4049 .to_point(&multi_buffer_snapshot);
4050 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
4051 multi_buffer_visible_start
4052 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
4053 Bias::Left,
4054 );
4055 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
4056 multi_buffer
4057 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
4058 .into_iter()
4059 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
4060 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
4061 let buffer = buffer_handle.read(cx);
4062 let buffer_file = project::File::from_dyn(buffer.file())?;
4063 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
4064 let worktree_entry = buffer_worktree
4065 .read(cx)
4066 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
4067 if worktree_entry.is_ignored {
4068 return None;
4069 }
4070
4071 let language = buffer.language()?;
4072 if let Some(restrict_to_languages) = restrict_to_languages {
4073 if !restrict_to_languages.contains(language) {
4074 return None;
4075 }
4076 }
4077 Some((
4078 excerpt_id,
4079 (
4080 buffer_handle,
4081 buffer.version().clone(),
4082 excerpt_visible_range,
4083 ),
4084 ))
4085 })
4086 .collect()
4087 }
4088
4089 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
4090 TextLayoutDetails {
4091 text_system: cx.text_system().clone(),
4092 editor_style: self.style.clone().unwrap(),
4093 rem_size: cx.rem_size(),
4094 scroll_anchor: self.scroll_manager.anchor(),
4095 visible_rows: self.visible_line_count(),
4096 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
4097 }
4098 }
4099
4100 fn splice_inlays(
4101 &self,
4102 to_remove: Vec<InlayId>,
4103 to_insert: Vec<Inlay>,
4104 cx: &mut ViewContext<Self>,
4105 ) {
4106 self.display_map.update(cx, |display_map, cx| {
4107 display_map.splice_inlays(to_remove, to_insert, cx);
4108 });
4109 cx.notify();
4110 }
4111
4112 fn trigger_on_type_formatting(
4113 &self,
4114 input: String,
4115 cx: &mut ViewContext<Self>,
4116 ) -> Option<Task<Result<()>>> {
4117 if input.len() != 1 {
4118 return None;
4119 }
4120
4121 let project = self.project.as_ref()?;
4122 let position = self.selections.newest_anchor().head();
4123 let (buffer, buffer_position) = self
4124 .buffer
4125 .read(cx)
4126 .text_anchor_for_position(position, cx)?;
4127
4128 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
4129 // hence we do LSP request & edit on host side only — add formats to host's history.
4130 let push_to_lsp_host_history = true;
4131 // If this is not the host, append its history with new edits.
4132 let push_to_client_history = project.read(cx).is_via_collab();
4133
4134 let on_type_formatting = project.update(cx, |project, cx| {
4135 project.on_type_format(
4136 buffer.clone(),
4137 buffer_position,
4138 input,
4139 push_to_lsp_host_history,
4140 cx,
4141 )
4142 });
4143 Some(cx.spawn(|editor, mut cx| async move {
4144 if let Some(transaction) = on_type_formatting.await? {
4145 if push_to_client_history {
4146 buffer
4147 .update(&mut cx, |buffer, _| {
4148 buffer.push_transaction(transaction, Instant::now());
4149 })
4150 .ok();
4151 }
4152 editor.update(&mut cx, |editor, cx| {
4153 editor.refresh_document_highlights(cx);
4154 })?;
4155 }
4156 Ok(())
4157 }))
4158 }
4159
4160 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
4161 if self.pending_rename.is_some() {
4162 return;
4163 }
4164
4165 let Some(provider) = self.completion_provider.as_ref() else {
4166 return;
4167 };
4168
4169 let position = self.selections.newest_anchor().head();
4170 let (buffer, buffer_position) =
4171 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4172 output
4173 } else {
4174 return;
4175 };
4176
4177 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4178 let is_followup_invoke = {
4179 let context_menu_state = self.context_menu.read();
4180 matches!(
4181 context_menu_state.deref(),
4182 Some(ContextMenu::Completions(_))
4183 )
4184 };
4185 let trigger_kind = match (&options.trigger, is_followup_invoke) {
4186 (_, true) => CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS,
4187 (Some(trigger), _) if buffer.read(cx).completion_triggers().contains(trigger) => {
4188 CompletionTriggerKind::TRIGGER_CHARACTER
4189 }
4190
4191 _ => CompletionTriggerKind::INVOKED,
4192 };
4193 let completion_context = CompletionContext {
4194 trigger_character: options.trigger.as_ref().and_then(|trigger| {
4195 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4196 Some(String::from(trigger))
4197 } else {
4198 None
4199 }
4200 }),
4201 trigger_kind,
4202 };
4203 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
4204 let sort_completions = provider.sort_completions();
4205
4206 let id = post_inc(&mut self.next_completion_id);
4207 let task = cx.spawn(|this, mut cx| {
4208 async move {
4209 this.update(&mut cx, |this, _| {
4210 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4211 })?;
4212 let completions = completions.await.log_err();
4213 let menu = if let Some(completions) = completions {
4214 let mut menu = CompletionsMenu {
4215 id,
4216 sort_completions,
4217 initial_position: position,
4218 match_candidates: completions
4219 .iter()
4220 .enumerate()
4221 .map(|(id, completion)| {
4222 StringMatchCandidate::new(
4223 id,
4224 completion.label.text[completion.label.filter_range.clone()]
4225 .into(),
4226 )
4227 })
4228 .collect(),
4229 buffer: buffer.clone(),
4230 completions: Arc::new(RwLock::new(completions.into())),
4231 matches: Vec::new().into(),
4232 selected_item: 0,
4233 scroll_handle: UniformListScrollHandle::new(),
4234 selected_completion_documentation_resolve_debounce: Arc::new(Mutex::new(
4235 DebouncedDelay::new(),
4236 )),
4237 };
4238 menu.filter(query.as_deref(), cx.background_executor().clone())
4239 .await;
4240
4241 if menu.matches.is_empty() {
4242 None
4243 } else {
4244 this.update(&mut cx, |editor, cx| {
4245 let completions = menu.completions.clone();
4246 let matches = menu.matches.clone();
4247
4248 let delay_ms = EditorSettings::get_global(cx)
4249 .completion_documentation_secondary_query_debounce;
4250 let delay = Duration::from_millis(delay_ms);
4251 editor
4252 .completion_documentation_pre_resolve_debounce
4253 .fire_new(delay, cx, |editor, cx| {
4254 CompletionsMenu::pre_resolve_completion_documentation(
4255 buffer,
4256 completions,
4257 matches,
4258 editor,
4259 cx,
4260 )
4261 });
4262 })
4263 .ok();
4264 Some(menu)
4265 }
4266 } else {
4267 None
4268 };
4269
4270 this.update(&mut cx, |this, cx| {
4271 let mut context_menu = this.context_menu.write();
4272 match context_menu.as_ref() {
4273 None => {}
4274
4275 Some(ContextMenu::Completions(prev_menu)) => {
4276 if prev_menu.id > id {
4277 return;
4278 }
4279 }
4280
4281 _ => return,
4282 }
4283
4284 if this.focus_handle.is_focused(cx) && menu.is_some() {
4285 let menu = menu.unwrap();
4286 *context_menu = Some(ContextMenu::Completions(menu));
4287 drop(context_menu);
4288 this.discard_inline_completion(false, cx);
4289 cx.notify();
4290 } else if this.completion_tasks.len() <= 1 {
4291 // If there are no more completion tasks and the last menu was
4292 // empty, we should hide it. If it was already hidden, we should
4293 // also show the copilot completion when available.
4294 drop(context_menu);
4295 if this.hide_context_menu(cx).is_none() {
4296 this.update_visible_inline_completion(cx);
4297 }
4298 }
4299 })?;
4300
4301 Ok::<_, anyhow::Error>(())
4302 }
4303 .log_err()
4304 });
4305
4306 self.completion_tasks.push((id, task));
4307 }
4308
4309 pub fn confirm_completion(
4310 &mut self,
4311 action: &ConfirmCompletion,
4312 cx: &mut ViewContext<Self>,
4313 ) -> Option<Task<Result<()>>> {
4314 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
4315 }
4316
4317 pub fn compose_completion(
4318 &mut self,
4319 action: &ComposeCompletion,
4320 cx: &mut ViewContext<Self>,
4321 ) -> Option<Task<Result<()>>> {
4322 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
4323 }
4324
4325 fn do_completion(
4326 &mut self,
4327 item_ix: Option<usize>,
4328 intent: CompletionIntent,
4329 cx: &mut ViewContext<Editor>,
4330 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4331 use language::ToOffset as _;
4332
4333 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
4334 menu
4335 } else {
4336 return None;
4337 };
4338
4339 let mat = completions_menu
4340 .matches
4341 .get(item_ix.unwrap_or(completions_menu.selected_item))?;
4342 let buffer_handle = completions_menu.buffer;
4343 let completions = completions_menu.completions.read();
4344 let completion = completions.get(mat.candidate_id)?;
4345 cx.stop_propagation();
4346
4347 let snippet;
4348 let text;
4349
4350 if completion.is_snippet() {
4351 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4352 text = snippet.as_ref().unwrap().text.clone();
4353 } else {
4354 snippet = None;
4355 text = completion.new_text.clone();
4356 };
4357 let selections = self.selections.all::<usize>(cx);
4358 let buffer = buffer_handle.read(cx);
4359 let old_range = completion.old_range.to_offset(buffer);
4360 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4361
4362 let newest_selection = self.selections.newest_anchor();
4363 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4364 return None;
4365 }
4366
4367 let lookbehind = newest_selection
4368 .start
4369 .text_anchor
4370 .to_offset(buffer)
4371 .saturating_sub(old_range.start);
4372 let lookahead = old_range
4373 .end
4374 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4375 let mut common_prefix_len = old_text
4376 .bytes()
4377 .zip(text.bytes())
4378 .take_while(|(a, b)| a == b)
4379 .count();
4380
4381 let snapshot = self.buffer.read(cx).snapshot(cx);
4382 let mut range_to_replace: Option<Range<isize>> = None;
4383 let mut ranges = Vec::new();
4384 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4385 for selection in &selections {
4386 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4387 let start = selection.start.saturating_sub(lookbehind);
4388 let end = selection.end + lookahead;
4389 if selection.id == newest_selection.id {
4390 range_to_replace = Some(
4391 ((start + common_prefix_len) as isize - selection.start as isize)
4392 ..(end as isize - selection.start as isize),
4393 );
4394 }
4395 ranges.push(start + common_prefix_len..end);
4396 } else {
4397 common_prefix_len = 0;
4398 ranges.clear();
4399 ranges.extend(selections.iter().map(|s| {
4400 if s.id == newest_selection.id {
4401 range_to_replace = Some(
4402 old_range.start.to_offset_utf16(&snapshot).0 as isize
4403 - selection.start as isize
4404 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4405 - selection.start as isize,
4406 );
4407 old_range.clone()
4408 } else {
4409 s.start..s.end
4410 }
4411 }));
4412 break;
4413 }
4414 if !self.linked_edit_ranges.is_empty() {
4415 let start_anchor = snapshot.anchor_before(selection.head());
4416 let end_anchor = snapshot.anchor_after(selection.tail());
4417 if let Some(ranges) = self
4418 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4419 {
4420 for (buffer, edits) in ranges {
4421 linked_edits.entry(buffer.clone()).or_default().extend(
4422 edits
4423 .into_iter()
4424 .map(|range| (range, text[common_prefix_len..].to_owned())),
4425 );
4426 }
4427 }
4428 }
4429 }
4430 let text = &text[common_prefix_len..];
4431
4432 cx.emit(EditorEvent::InputHandled {
4433 utf16_range_to_replace: range_to_replace,
4434 text: text.into(),
4435 });
4436
4437 self.transact(cx, |this, cx| {
4438 if let Some(mut snippet) = snippet {
4439 snippet.text = text.to_string();
4440 for tabstop in snippet.tabstops.iter_mut().flatten() {
4441 tabstop.start -= common_prefix_len as isize;
4442 tabstop.end -= common_prefix_len as isize;
4443 }
4444
4445 this.insert_snippet(&ranges, snippet, cx).log_err();
4446 } else {
4447 this.buffer.update(cx, |buffer, cx| {
4448 buffer.edit(
4449 ranges.iter().map(|range| (range.clone(), text)),
4450 this.autoindent_mode.clone(),
4451 cx,
4452 );
4453 });
4454 }
4455 for (buffer, edits) in linked_edits {
4456 buffer.update(cx, |buffer, cx| {
4457 let snapshot = buffer.snapshot();
4458 let edits = edits
4459 .into_iter()
4460 .map(|(range, text)| {
4461 use text::ToPoint as TP;
4462 let end_point = TP::to_point(&range.end, &snapshot);
4463 let start_point = TP::to_point(&range.start, &snapshot);
4464 (start_point..end_point, text)
4465 })
4466 .sorted_by_key(|(range, _)| range.start)
4467 .collect::<Vec<_>>();
4468 buffer.edit(edits, None, cx);
4469 })
4470 }
4471
4472 this.refresh_inline_completion(true, false, cx);
4473 });
4474
4475 let show_new_completions_on_confirm = completion
4476 .confirm
4477 .as_ref()
4478 .map_or(false, |confirm| confirm(intent, cx));
4479 if show_new_completions_on_confirm {
4480 self.show_completions(&ShowCompletions { trigger: None }, cx);
4481 }
4482
4483 let provider = self.completion_provider.as_ref()?;
4484 let apply_edits = provider.apply_additional_edits_for_completion(
4485 buffer_handle,
4486 completion.clone(),
4487 true,
4488 cx,
4489 );
4490
4491 let editor_settings = EditorSettings::get_global(cx);
4492 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4493 // After the code completion is finished, users often want to know what signatures are needed.
4494 // so we should automatically call signature_help
4495 self.show_signature_help(&ShowSignatureHelp, cx);
4496 }
4497
4498 Some(cx.foreground_executor().spawn(async move {
4499 apply_edits.await?;
4500 Ok(())
4501 }))
4502 }
4503
4504 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4505 let mut context_menu = self.context_menu.write();
4506 if let Some(ContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4507 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4508 // Toggle if we're selecting the same one
4509 *context_menu = None;
4510 cx.notify();
4511 return;
4512 } else {
4513 // Otherwise, clear it and start a new one
4514 *context_menu = None;
4515 cx.notify();
4516 }
4517 }
4518 drop(context_menu);
4519 let snapshot = self.snapshot(cx);
4520 let deployed_from_indicator = action.deployed_from_indicator;
4521 let mut task = self.code_actions_task.take();
4522 let action = action.clone();
4523 cx.spawn(|editor, mut cx| async move {
4524 while let Some(prev_task) = task {
4525 prev_task.await;
4526 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4527 }
4528
4529 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4530 if editor.focus_handle.is_focused(cx) {
4531 let multibuffer_point = action
4532 .deployed_from_indicator
4533 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4534 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4535 let (buffer, buffer_row) = snapshot
4536 .buffer_snapshot
4537 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4538 .and_then(|(buffer_snapshot, range)| {
4539 editor
4540 .buffer
4541 .read(cx)
4542 .buffer(buffer_snapshot.remote_id())
4543 .map(|buffer| (buffer, range.start.row))
4544 })?;
4545 let (_, code_actions) = editor
4546 .available_code_actions
4547 .clone()
4548 .and_then(|(location, code_actions)| {
4549 let snapshot = location.buffer.read(cx).snapshot();
4550 let point_range = location.range.to_point(&snapshot);
4551 let point_range = point_range.start.row..=point_range.end.row;
4552 if point_range.contains(&buffer_row) {
4553 Some((location, code_actions))
4554 } else {
4555 None
4556 }
4557 })
4558 .unzip();
4559 let buffer_id = buffer.read(cx).remote_id();
4560 let tasks = editor
4561 .tasks
4562 .get(&(buffer_id, buffer_row))
4563 .map(|t| Arc::new(t.to_owned()));
4564 if tasks.is_none() && code_actions.is_none() {
4565 return None;
4566 }
4567
4568 editor.completion_tasks.clear();
4569 editor.discard_inline_completion(false, cx);
4570 let task_context =
4571 tasks
4572 .as_ref()
4573 .zip(editor.project.clone())
4574 .map(|(tasks, project)| {
4575 let position = Point::new(buffer_row, tasks.column);
4576 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
4577 let location = Location {
4578 buffer: buffer.clone(),
4579 range: range_start..range_start,
4580 };
4581 // Fill in the environmental variables from the tree-sitter captures
4582 let mut captured_task_variables = TaskVariables::default();
4583 for (capture_name, value) in tasks.extra_variables.clone() {
4584 captured_task_variables.insert(
4585 task::VariableName::Custom(capture_name.into()),
4586 value.clone(),
4587 );
4588 }
4589 project.update(cx, |project, cx| {
4590 project.task_context_for_location(
4591 captured_task_variables,
4592 location,
4593 cx,
4594 )
4595 })
4596 });
4597
4598 Some(cx.spawn(|editor, mut cx| async move {
4599 let task_context = match task_context {
4600 Some(task_context) => task_context.await,
4601 None => None,
4602 };
4603 let resolved_tasks =
4604 tasks.zip(task_context).map(|(tasks, task_context)| {
4605 Arc::new(ResolvedTasks {
4606 templates: tasks
4607 .templates
4608 .iter()
4609 .filter_map(|(kind, template)| {
4610 template
4611 .resolve_task(&kind.to_id_base(), &task_context)
4612 .map(|task| (kind.clone(), task))
4613 })
4614 .collect(),
4615 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4616 multibuffer_point.row,
4617 tasks.column,
4618 )),
4619 })
4620 });
4621 let spawn_straight_away = resolved_tasks
4622 .as_ref()
4623 .map_or(false, |tasks| tasks.templates.len() == 1)
4624 && code_actions
4625 .as_ref()
4626 .map_or(true, |actions| actions.is_empty());
4627 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4628 *editor.context_menu.write() =
4629 Some(ContextMenu::CodeActions(CodeActionsMenu {
4630 buffer,
4631 actions: CodeActionContents {
4632 tasks: resolved_tasks,
4633 actions: code_actions,
4634 },
4635 selected_item: Default::default(),
4636 scroll_handle: UniformListScrollHandle::default(),
4637 deployed_from_indicator,
4638 }));
4639 if spawn_straight_away {
4640 if let Some(task) = editor.confirm_code_action(
4641 &ConfirmCodeAction { item_ix: Some(0) },
4642 cx,
4643 ) {
4644 cx.notify();
4645 return task;
4646 }
4647 }
4648 cx.notify();
4649 Task::ready(Ok(()))
4650 }) {
4651 task.await
4652 } else {
4653 Ok(())
4654 }
4655 }))
4656 } else {
4657 Some(Task::ready(Ok(())))
4658 }
4659 })?;
4660 if let Some(task) = spawned_test_task {
4661 task.await?;
4662 }
4663
4664 Ok::<_, anyhow::Error>(())
4665 })
4666 .detach_and_log_err(cx);
4667 }
4668
4669 pub fn confirm_code_action(
4670 &mut self,
4671 action: &ConfirmCodeAction,
4672 cx: &mut ViewContext<Self>,
4673 ) -> Option<Task<Result<()>>> {
4674 let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4675 menu
4676 } else {
4677 return None;
4678 };
4679 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4680 let action = actions_menu.actions.get(action_ix)?;
4681 let title = action.label();
4682 let buffer = actions_menu.buffer;
4683 let workspace = self.workspace()?;
4684
4685 match action {
4686 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4687 workspace.update(cx, |workspace, cx| {
4688 workspace::tasks::schedule_resolved_task(
4689 workspace,
4690 task_source_kind,
4691 resolved_task,
4692 false,
4693 cx,
4694 );
4695
4696 Some(Task::ready(Ok(())))
4697 })
4698 }
4699 CodeActionsItem::CodeAction(action) => {
4700 let apply_code_actions = workspace
4701 .read(cx)
4702 .project()
4703 .clone()
4704 .update(cx, |project, cx| {
4705 project.apply_code_action(buffer, action, true, cx)
4706 });
4707 let workspace = workspace.downgrade();
4708 Some(cx.spawn(|editor, cx| async move {
4709 let project_transaction = apply_code_actions.await?;
4710 Self::open_project_transaction(
4711 &editor,
4712 workspace,
4713 project_transaction,
4714 title,
4715 cx,
4716 )
4717 .await
4718 }))
4719 }
4720 }
4721 }
4722
4723 pub async fn open_project_transaction(
4724 this: &WeakView<Editor>,
4725 workspace: WeakView<Workspace>,
4726 transaction: ProjectTransaction,
4727 title: String,
4728 mut cx: AsyncWindowContext,
4729 ) -> Result<()> {
4730 let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
4731
4732 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4733 cx.update(|cx| {
4734 entries.sort_unstable_by_key(|(buffer, _)| {
4735 buffer.read(cx).file().map(|f| f.path().clone())
4736 });
4737 })?;
4738
4739 // If the project transaction's edits are all contained within this editor, then
4740 // avoid opening a new editor to display them.
4741
4742 if let Some((buffer, transaction)) = entries.first() {
4743 if entries.len() == 1 {
4744 let excerpt = this.update(&mut cx, |editor, cx| {
4745 editor
4746 .buffer()
4747 .read(cx)
4748 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4749 })?;
4750 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4751 if excerpted_buffer == *buffer {
4752 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4753 let excerpt_range = excerpt_range.to_offset(buffer);
4754 buffer
4755 .edited_ranges_for_transaction::<usize>(transaction)
4756 .all(|range| {
4757 excerpt_range.start <= range.start
4758 && excerpt_range.end >= range.end
4759 })
4760 })?;
4761
4762 if all_edits_within_excerpt {
4763 return Ok(());
4764 }
4765 }
4766 }
4767 }
4768 } else {
4769 return Ok(());
4770 }
4771
4772 let mut ranges_to_highlight = Vec::new();
4773 let excerpt_buffer = cx.new_model(|cx| {
4774 let mut multibuffer =
4775 MultiBuffer::new(replica_id, Capability::ReadWrite).with_title(title);
4776 for (buffer_handle, transaction) in &entries {
4777 let buffer = buffer_handle.read(cx);
4778 ranges_to_highlight.extend(
4779 multibuffer.push_excerpts_with_context_lines(
4780 buffer_handle.clone(),
4781 buffer
4782 .edited_ranges_for_transaction::<usize>(transaction)
4783 .collect(),
4784 DEFAULT_MULTIBUFFER_CONTEXT,
4785 cx,
4786 ),
4787 );
4788 }
4789 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4790 multibuffer
4791 })?;
4792
4793 workspace.update(&mut cx, |workspace, cx| {
4794 let project = workspace.project().clone();
4795 let editor =
4796 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4797 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4798 editor.update(cx, |editor, cx| {
4799 editor.highlight_background::<Self>(
4800 &ranges_to_highlight,
4801 |theme| theme.editor_highlighted_line_background,
4802 cx,
4803 );
4804 });
4805 })?;
4806
4807 Ok(())
4808 }
4809
4810 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4811 let project = self.project.clone()?;
4812 let buffer = self.buffer.read(cx);
4813 let newest_selection = self.selections.newest_anchor().clone();
4814 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4815 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4816 if start_buffer != end_buffer {
4817 return None;
4818 }
4819
4820 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4821 cx.background_executor()
4822 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4823 .await;
4824
4825 let actions = if let Ok(code_actions) = project.update(&mut cx, |project, cx| {
4826 project.code_actions(&start_buffer, start..end, cx)
4827 }) {
4828 code_actions.await
4829 } else {
4830 Vec::new()
4831 };
4832
4833 this.update(&mut cx, |this, cx| {
4834 this.available_code_actions = if actions.is_empty() {
4835 None
4836 } else {
4837 Some((
4838 Location {
4839 buffer: start_buffer,
4840 range: start..end,
4841 },
4842 actions.into(),
4843 ))
4844 };
4845 cx.notify();
4846 })
4847 .log_err();
4848 }));
4849 None
4850 }
4851
4852 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4853 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4854 self.show_git_blame_inline = false;
4855
4856 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4857 cx.background_executor().timer(delay).await;
4858
4859 this.update(&mut cx, |this, cx| {
4860 this.show_git_blame_inline = true;
4861 cx.notify();
4862 })
4863 .log_err();
4864 }));
4865 }
4866 }
4867
4868 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4869 if self.pending_rename.is_some() {
4870 return None;
4871 }
4872
4873 let project = self.project.clone()?;
4874 let buffer = self.buffer.read(cx);
4875 let newest_selection = self.selections.newest_anchor().clone();
4876 let cursor_position = newest_selection.head();
4877 let (cursor_buffer, cursor_buffer_position) =
4878 buffer.text_anchor_for_position(cursor_position, cx)?;
4879 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4880 if cursor_buffer != tail_buffer {
4881 return None;
4882 }
4883
4884 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4885 cx.background_executor()
4886 .timer(DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT)
4887 .await;
4888
4889 let highlights = if let Some(highlights) = project
4890 .update(&mut cx, |project, cx| {
4891 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4892 })
4893 .log_err()
4894 {
4895 highlights.await.log_err()
4896 } else {
4897 None
4898 };
4899
4900 if let Some(highlights) = highlights {
4901 this.update(&mut cx, |this, cx| {
4902 if this.pending_rename.is_some() {
4903 return;
4904 }
4905
4906 let buffer_id = cursor_position.buffer_id;
4907 let buffer = this.buffer.read(cx);
4908 if !buffer
4909 .text_anchor_for_position(cursor_position, cx)
4910 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4911 {
4912 return;
4913 }
4914
4915 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4916 let mut write_ranges = Vec::new();
4917 let mut read_ranges = Vec::new();
4918 for highlight in highlights {
4919 for (excerpt_id, excerpt_range) in
4920 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4921 {
4922 let start = highlight
4923 .range
4924 .start
4925 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4926 let end = highlight
4927 .range
4928 .end
4929 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4930 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4931 continue;
4932 }
4933
4934 let range = Anchor {
4935 buffer_id,
4936 excerpt_id,
4937 text_anchor: start,
4938 }..Anchor {
4939 buffer_id,
4940 excerpt_id,
4941 text_anchor: end,
4942 };
4943 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4944 write_ranges.push(range);
4945 } else {
4946 read_ranges.push(range);
4947 }
4948 }
4949 }
4950
4951 this.highlight_background::<DocumentHighlightRead>(
4952 &read_ranges,
4953 |theme| theme.editor_document_highlight_read_background,
4954 cx,
4955 );
4956 this.highlight_background::<DocumentHighlightWrite>(
4957 &write_ranges,
4958 |theme| theme.editor_document_highlight_write_background,
4959 cx,
4960 );
4961 cx.notify();
4962 })
4963 .log_err();
4964 }
4965 }));
4966 None
4967 }
4968
4969 pub fn refresh_inline_completion(
4970 &mut self,
4971 debounce: bool,
4972 user_requested: bool,
4973 cx: &mut ViewContext<Self>,
4974 ) -> Option<()> {
4975 let provider = self.inline_completion_provider()?;
4976 let cursor = self.selections.newest_anchor().head();
4977 let (buffer, cursor_buffer_position) =
4978 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4979
4980 if !user_requested
4981 && (!self.enable_inline_completions
4982 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx))
4983 {
4984 self.discard_inline_completion(false, cx);
4985 return None;
4986 }
4987
4988 self.update_visible_inline_completion(cx);
4989 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4990 Some(())
4991 }
4992
4993 fn cycle_inline_completion(
4994 &mut self,
4995 direction: Direction,
4996 cx: &mut ViewContext<Self>,
4997 ) -> Option<()> {
4998 let provider = self.inline_completion_provider()?;
4999 let cursor = self.selections.newest_anchor().head();
5000 let (buffer, cursor_buffer_position) =
5001 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5002 if !self.enable_inline_completions
5003 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
5004 {
5005 return None;
5006 }
5007
5008 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5009 self.update_visible_inline_completion(cx);
5010
5011 Some(())
5012 }
5013
5014 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
5015 if !self.has_active_inline_completion(cx) {
5016 self.refresh_inline_completion(false, true, cx);
5017 return;
5018 }
5019
5020 self.update_visible_inline_completion(cx);
5021 }
5022
5023 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
5024 self.show_cursor_names(cx);
5025 }
5026
5027 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
5028 self.show_cursor_names = true;
5029 cx.notify();
5030 cx.spawn(|this, mut cx| async move {
5031 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5032 this.update(&mut cx, |this, cx| {
5033 this.show_cursor_names = false;
5034 cx.notify()
5035 })
5036 .ok()
5037 })
5038 .detach();
5039 }
5040
5041 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
5042 if self.has_active_inline_completion(cx) {
5043 self.cycle_inline_completion(Direction::Next, cx);
5044 } else {
5045 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5046 if is_copilot_disabled {
5047 cx.propagate();
5048 }
5049 }
5050 }
5051
5052 pub fn previous_inline_completion(
5053 &mut self,
5054 _: &PreviousInlineCompletion,
5055 cx: &mut ViewContext<Self>,
5056 ) {
5057 if self.has_active_inline_completion(cx) {
5058 self.cycle_inline_completion(Direction::Prev, cx);
5059 } else {
5060 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
5061 if is_copilot_disabled {
5062 cx.propagate();
5063 }
5064 }
5065 }
5066
5067 pub fn accept_inline_completion(
5068 &mut self,
5069 _: &AcceptInlineCompletion,
5070 cx: &mut ViewContext<Self>,
5071 ) {
5072 let Some((completion, delete_range)) = self.take_active_inline_completion(cx) else {
5073 return;
5074 };
5075 if let Some(provider) = self.inline_completion_provider() {
5076 provider.accept(cx);
5077 }
5078
5079 cx.emit(EditorEvent::InputHandled {
5080 utf16_range_to_replace: None,
5081 text: completion.text.to_string().into(),
5082 });
5083
5084 if let Some(range) = delete_range {
5085 self.change_selections(None, cx, |s| s.select_ranges([range]))
5086 }
5087 self.insert_with_autoindent_mode(&completion.text.to_string(), None, cx);
5088 self.refresh_inline_completion(true, true, cx);
5089 cx.notify();
5090 }
5091
5092 pub fn accept_partial_inline_completion(
5093 &mut self,
5094 _: &AcceptPartialInlineCompletion,
5095 cx: &mut ViewContext<Self>,
5096 ) {
5097 if self.selections.count() == 1 && self.has_active_inline_completion(cx) {
5098 if let Some((completion, delete_range)) = self.take_active_inline_completion(cx) {
5099 let mut partial_completion = completion
5100 .text
5101 .chars()
5102 .by_ref()
5103 .take_while(|c| c.is_alphabetic())
5104 .collect::<String>();
5105 if partial_completion.is_empty() {
5106 partial_completion = completion
5107 .text
5108 .chars()
5109 .by_ref()
5110 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5111 .collect::<String>();
5112 }
5113
5114 cx.emit(EditorEvent::InputHandled {
5115 utf16_range_to_replace: None,
5116 text: partial_completion.clone().into(),
5117 });
5118
5119 if let Some(range) = delete_range {
5120 self.change_selections(None, cx, |s| s.select_ranges([range]))
5121 }
5122 self.insert_with_autoindent_mode(&partial_completion, None, cx);
5123
5124 self.refresh_inline_completion(true, true, cx);
5125 cx.notify();
5126 }
5127 }
5128 }
5129
5130 fn discard_inline_completion(
5131 &mut self,
5132 should_report_inline_completion_event: bool,
5133 cx: &mut ViewContext<Self>,
5134 ) -> bool {
5135 if let Some(provider) = self.inline_completion_provider() {
5136 provider.discard(should_report_inline_completion_event, cx);
5137 }
5138
5139 self.take_active_inline_completion(cx).is_some()
5140 }
5141
5142 pub fn has_active_inline_completion(&self, cx: &AppContext) -> bool {
5143 if let Some(completion) = self.active_inline_completion.as_ref() {
5144 let buffer = self.buffer.read(cx).read(cx);
5145 completion.0.position.is_valid(&buffer)
5146 } else {
5147 false
5148 }
5149 }
5150
5151 fn take_active_inline_completion(
5152 &mut self,
5153 cx: &mut ViewContext<Self>,
5154 ) -> Option<(Inlay, Option<Range<Anchor>>)> {
5155 let completion = self.active_inline_completion.take()?;
5156 self.display_map.update(cx, |map, cx| {
5157 map.splice_inlays(vec![completion.0.id], Default::default(), cx);
5158 });
5159 let buffer = self.buffer.read(cx).read(cx);
5160
5161 if completion.0.position.is_valid(&buffer) {
5162 Some(completion)
5163 } else {
5164 None
5165 }
5166 }
5167
5168 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) {
5169 let selection = self.selections.newest_anchor();
5170 let cursor = selection.head();
5171
5172 let excerpt_id = cursor.excerpt_id;
5173
5174 if self.context_menu.read().is_none()
5175 && self.completion_tasks.is_empty()
5176 && selection.start == selection.end
5177 {
5178 if let Some(provider) = self.inline_completion_provider() {
5179 if let Some((buffer, cursor_buffer_position)) =
5180 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5181 {
5182 if let Some((text, text_anchor_range)) =
5183 provider.active_completion_text(&buffer, cursor_buffer_position, cx)
5184 {
5185 let text = Rope::from(text);
5186 let mut to_remove = Vec::new();
5187 if let Some(completion) = self.active_inline_completion.take() {
5188 to_remove.push(completion.0.id);
5189 }
5190
5191 let completion_inlay =
5192 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
5193
5194 let multibuffer_anchor_range = text_anchor_range.and_then(|range| {
5195 let snapshot = self.buffer.read(cx).snapshot(cx);
5196 Some(
5197 snapshot.anchor_in_excerpt(excerpt_id, range.start)?
5198 ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?,
5199 )
5200 });
5201 self.active_inline_completion =
5202 Some((completion_inlay.clone(), multibuffer_anchor_range));
5203
5204 self.display_map.update(cx, move |map, cx| {
5205 map.splice_inlays(to_remove, vec![completion_inlay], cx)
5206 });
5207 cx.notify();
5208 return;
5209 }
5210 }
5211 }
5212 }
5213
5214 self.discard_inline_completion(false, cx);
5215 }
5216
5217 fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5218 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5219 }
5220
5221 fn render_code_actions_indicator(
5222 &self,
5223 _style: &EditorStyle,
5224 row: DisplayRow,
5225 is_active: bool,
5226 cx: &mut ViewContext<Self>,
5227 ) -> Option<IconButton> {
5228 if self.available_code_actions.is_some() {
5229 Some(
5230 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5231 .shape(ui::IconButtonShape::Square)
5232 .icon_size(IconSize::XSmall)
5233 .icon_color(Color::Muted)
5234 .selected(is_active)
5235 .on_click(cx.listener(move |editor, _e, cx| {
5236 editor.focus(cx);
5237 editor.toggle_code_actions(
5238 &ToggleCodeActions {
5239 deployed_from_indicator: Some(row),
5240 },
5241 cx,
5242 );
5243 })),
5244 )
5245 } else {
5246 None
5247 }
5248 }
5249
5250 fn clear_tasks(&mut self) {
5251 self.tasks.clear()
5252 }
5253
5254 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5255 if self.tasks.insert(key, value).is_some() {
5256 // This case should hopefully be rare, but just in case...
5257 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5258 }
5259 }
5260
5261 fn render_run_indicator(
5262 &self,
5263 _style: &EditorStyle,
5264 is_active: bool,
5265 row: DisplayRow,
5266 cx: &mut ViewContext<Self>,
5267 ) -> IconButton {
5268 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5269 .shape(ui::IconButtonShape::Square)
5270 .icon_size(IconSize::XSmall)
5271 .icon_color(Color::Muted)
5272 .selected(is_active)
5273 .on_click(cx.listener(move |editor, _e, cx| {
5274 editor.focus(cx);
5275 editor.toggle_code_actions(
5276 &ToggleCodeActions {
5277 deployed_from_indicator: Some(row),
5278 },
5279 cx,
5280 );
5281 }))
5282 }
5283
5284 fn close_hunk_diff_button(
5285 &self,
5286 hunk: HoveredHunk,
5287 row: DisplayRow,
5288 cx: &mut ViewContext<Self>,
5289 ) -> IconButton {
5290 IconButton::new(
5291 ("close_hunk_diff_indicator", row.0 as usize),
5292 ui::IconName::Close,
5293 )
5294 .shape(ui::IconButtonShape::Square)
5295 .icon_size(IconSize::XSmall)
5296 .icon_color(Color::Muted)
5297 .tooltip(|cx| Tooltip::for_action("Close hunk diff", &ToggleHunkDiff, cx))
5298 .on_click(cx.listener(move |editor, _e, cx| editor.toggle_hovered_hunk(&hunk, cx)))
5299 }
5300
5301 pub fn context_menu_visible(&self) -> bool {
5302 self.context_menu
5303 .read()
5304 .as_ref()
5305 .map_or(false, |menu| menu.visible())
5306 }
5307
5308 fn render_context_menu(
5309 &self,
5310 cursor_position: DisplayPoint,
5311 style: &EditorStyle,
5312 max_height: Pixels,
5313 cx: &mut ViewContext<Editor>,
5314 ) -> Option<(ContextMenuOrigin, AnyElement)> {
5315 self.context_menu.read().as_ref().map(|menu| {
5316 menu.render(
5317 cursor_position,
5318 style,
5319 max_height,
5320 self.workspace.as_ref().map(|(w, _)| w.clone()),
5321 cx,
5322 )
5323 })
5324 }
5325
5326 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
5327 cx.notify();
5328 self.completion_tasks.clear();
5329 let context_menu = self.context_menu.write().take();
5330 if context_menu.is_some() {
5331 self.update_visible_inline_completion(cx);
5332 }
5333 context_menu
5334 }
5335
5336 pub fn insert_snippet(
5337 &mut self,
5338 insertion_ranges: &[Range<usize>],
5339 snippet: Snippet,
5340 cx: &mut ViewContext<Self>,
5341 ) -> Result<()> {
5342 struct Tabstop<T> {
5343 is_end_tabstop: bool,
5344 ranges: Vec<Range<T>>,
5345 }
5346
5347 let tabstops = self.buffer.update(cx, |buffer, cx| {
5348 let snippet_text: Arc<str> = snippet.text.clone().into();
5349 buffer.edit(
5350 insertion_ranges
5351 .iter()
5352 .cloned()
5353 .map(|range| (range, snippet_text.clone())),
5354 Some(AutoindentMode::EachLine),
5355 cx,
5356 );
5357
5358 let snapshot = &*buffer.read(cx);
5359 let snippet = &snippet;
5360 snippet
5361 .tabstops
5362 .iter()
5363 .map(|tabstop| {
5364 let is_end_tabstop = tabstop.first().map_or(false, |tabstop| {
5365 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5366 });
5367 let mut tabstop_ranges = tabstop
5368 .iter()
5369 .flat_map(|tabstop_range| {
5370 let mut delta = 0_isize;
5371 insertion_ranges.iter().map(move |insertion_range| {
5372 let insertion_start = insertion_range.start as isize + delta;
5373 delta +=
5374 snippet.text.len() as isize - insertion_range.len() as isize;
5375
5376 let start = ((insertion_start + tabstop_range.start) as usize)
5377 .min(snapshot.len());
5378 let end = ((insertion_start + tabstop_range.end) as usize)
5379 .min(snapshot.len());
5380 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5381 })
5382 })
5383 .collect::<Vec<_>>();
5384 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5385
5386 Tabstop {
5387 is_end_tabstop,
5388 ranges: tabstop_ranges,
5389 }
5390 })
5391 .collect::<Vec<_>>()
5392 });
5393 if let Some(tabstop) = tabstops.first() {
5394 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5395 s.select_ranges(tabstop.ranges.iter().cloned());
5396 });
5397
5398 // If we're already at the last tabstop and it's at the end of the snippet,
5399 // we're done, we don't need to keep the state around.
5400 if !tabstop.is_end_tabstop {
5401 let ranges = tabstops
5402 .into_iter()
5403 .map(|tabstop| tabstop.ranges)
5404 .collect::<Vec<_>>();
5405 self.snippet_stack.push(SnippetState {
5406 active_index: 0,
5407 ranges,
5408 });
5409 }
5410
5411 // Check whether the just-entered snippet ends with an auto-closable bracket.
5412 if self.autoclose_regions.is_empty() {
5413 let snapshot = self.buffer.read(cx).snapshot(cx);
5414 for selection in &mut self.selections.all::<Point>(cx) {
5415 let selection_head = selection.head();
5416 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5417 continue;
5418 };
5419
5420 let mut bracket_pair = None;
5421 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5422 let prev_chars = snapshot
5423 .reversed_chars_at(selection_head)
5424 .collect::<String>();
5425 for (pair, enabled) in scope.brackets() {
5426 if enabled
5427 && pair.close
5428 && prev_chars.starts_with(pair.start.as_str())
5429 && next_chars.starts_with(pair.end.as_str())
5430 {
5431 bracket_pair = Some(pair.clone());
5432 break;
5433 }
5434 }
5435 if let Some(pair) = bracket_pair {
5436 let start = snapshot.anchor_after(selection_head);
5437 let end = snapshot.anchor_after(selection_head);
5438 self.autoclose_regions.push(AutocloseRegion {
5439 selection_id: selection.id,
5440 range: start..end,
5441 pair,
5442 });
5443 }
5444 }
5445 }
5446 }
5447 Ok(())
5448 }
5449
5450 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5451 self.move_to_snippet_tabstop(Bias::Right, cx)
5452 }
5453
5454 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5455 self.move_to_snippet_tabstop(Bias::Left, cx)
5456 }
5457
5458 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5459 if let Some(mut snippet) = self.snippet_stack.pop() {
5460 match bias {
5461 Bias::Left => {
5462 if snippet.active_index > 0 {
5463 snippet.active_index -= 1;
5464 } else {
5465 self.snippet_stack.push(snippet);
5466 return false;
5467 }
5468 }
5469 Bias::Right => {
5470 if snippet.active_index + 1 < snippet.ranges.len() {
5471 snippet.active_index += 1;
5472 } else {
5473 self.snippet_stack.push(snippet);
5474 return false;
5475 }
5476 }
5477 }
5478 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5479 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5480 s.select_anchor_ranges(current_ranges.iter().cloned())
5481 });
5482 // If snippet state is not at the last tabstop, push it back on the stack
5483 if snippet.active_index + 1 < snippet.ranges.len() {
5484 self.snippet_stack.push(snippet);
5485 }
5486 return true;
5487 }
5488 }
5489
5490 false
5491 }
5492
5493 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5494 self.transact(cx, |this, cx| {
5495 this.select_all(&SelectAll, cx);
5496 this.insert("", cx);
5497 });
5498 }
5499
5500 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5501 self.transact(cx, |this, cx| {
5502 this.select_autoclose_pair(cx);
5503 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5504 if !this.linked_edit_ranges.is_empty() {
5505 let selections = this.selections.all::<MultiBufferPoint>(cx);
5506 let snapshot = this.buffer.read(cx).snapshot(cx);
5507
5508 for selection in selections.iter() {
5509 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5510 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5511 if selection_start.buffer_id != selection_end.buffer_id {
5512 continue;
5513 }
5514 if let Some(ranges) =
5515 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5516 {
5517 for (buffer, entries) in ranges {
5518 linked_ranges.entry(buffer).or_default().extend(entries);
5519 }
5520 }
5521 }
5522 }
5523
5524 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5525 if !this.selections.line_mode {
5526 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5527 for selection in &mut selections {
5528 if selection.is_empty() {
5529 let old_head = selection.head();
5530 let mut new_head =
5531 movement::left(&display_map, old_head.to_display_point(&display_map))
5532 .to_point(&display_map);
5533 if let Some((buffer, line_buffer_range)) = display_map
5534 .buffer_snapshot
5535 .buffer_line_for_row(MultiBufferRow(old_head.row))
5536 {
5537 let indent_size =
5538 buffer.indent_size_for_line(line_buffer_range.start.row);
5539 let indent_len = match indent_size.kind {
5540 IndentKind::Space => {
5541 buffer.settings_at(line_buffer_range.start, cx).tab_size
5542 }
5543 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5544 };
5545 if old_head.column <= indent_size.len && old_head.column > 0 {
5546 let indent_len = indent_len.get();
5547 new_head = cmp::min(
5548 new_head,
5549 MultiBufferPoint::new(
5550 old_head.row,
5551 ((old_head.column - 1) / indent_len) * indent_len,
5552 ),
5553 );
5554 }
5555 }
5556
5557 selection.set_head(new_head, SelectionGoal::None);
5558 }
5559 }
5560 }
5561
5562 this.signature_help_state.set_backspace_pressed(true);
5563 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5564 this.insert("", cx);
5565 let empty_str: Arc<str> = Arc::from("");
5566 for (buffer, edits) in linked_ranges {
5567 let snapshot = buffer.read(cx).snapshot();
5568 use text::ToPoint as TP;
5569
5570 let edits = edits
5571 .into_iter()
5572 .map(|range| {
5573 let end_point = TP::to_point(&range.end, &snapshot);
5574 let mut start_point = TP::to_point(&range.start, &snapshot);
5575
5576 if end_point == start_point {
5577 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5578 .saturating_sub(1);
5579 start_point = TP::to_point(&offset, &snapshot);
5580 };
5581
5582 (start_point..end_point, empty_str.clone())
5583 })
5584 .sorted_by_key(|(range, _)| range.start)
5585 .collect::<Vec<_>>();
5586 buffer.update(cx, |this, cx| {
5587 this.edit(edits, None, cx);
5588 })
5589 }
5590 this.refresh_inline_completion(true, false, cx);
5591 linked_editing_ranges::refresh_linked_ranges(this, cx);
5592 });
5593 }
5594
5595 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5596 self.transact(cx, |this, cx| {
5597 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5598 let line_mode = s.line_mode;
5599 s.move_with(|map, selection| {
5600 if selection.is_empty() && !line_mode {
5601 let cursor = movement::right(map, selection.head());
5602 selection.end = cursor;
5603 selection.reversed = true;
5604 selection.goal = SelectionGoal::None;
5605 }
5606 })
5607 });
5608 this.insert("", cx);
5609 this.refresh_inline_completion(true, false, cx);
5610 });
5611 }
5612
5613 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5614 if self.move_to_prev_snippet_tabstop(cx) {
5615 return;
5616 }
5617
5618 self.outdent(&Outdent, cx);
5619 }
5620
5621 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5622 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5623 return;
5624 }
5625
5626 let mut selections = self.selections.all_adjusted(cx);
5627 let buffer = self.buffer.read(cx);
5628 let snapshot = buffer.snapshot(cx);
5629 let rows_iter = selections.iter().map(|s| s.head().row);
5630 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5631
5632 let mut edits = Vec::new();
5633 let mut prev_edited_row = 0;
5634 let mut row_delta = 0;
5635 for selection in &mut selections {
5636 if selection.start.row != prev_edited_row {
5637 row_delta = 0;
5638 }
5639 prev_edited_row = selection.end.row;
5640
5641 // If the selection is non-empty, then increase the indentation of the selected lines.
5642 if !selection.is_empty() {
5643 row_delta =
5644 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5645 continue;
5646 }
5647
5648 // If the selection is empty and the cursor is in the leading whitespace before the
5649 // suggested indentation, then auto-indent the line.
5650 let cursor = selection.head();
5651 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5652 if let Some(suggested_indent) =
5653 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5654 {
5655 if cursor.column < suggested_indent.len
5656 && cursor.column <= current_indent.len
5657 && current_indent.len <= suggested_indent.len
5658 {
5659 selection.start = Point::new(cursor.row, suggested_indent.len);
5660 selection.end = selection.start;
5661 if row_delta == 0 {
5662 edits.extend(Buffer::edit_for_indent_size_adjustment(
5663 cursor.row,
5664 current_indent,
5665 suggested_indent,
5666 ));
5667 row_delta = suggested_indent.len - current_indent.len;
5668 }
5669 continue;
5670 }
5671 }
5672
5673 // Otherwise, insert a hard or soft tab.
5674 let settings = buffer.settings_at(cursor, cx);
5675 let tab_size = if settings.hard_tabs {
5676 IndentSize::tab()
5677 } else {
5678 let tab_size = settings.tab_size.get();
5679 let char_column = snapshot
5680 .text_for_range(Point::new(cursor.row, 0)..cursor)
5681 .flat_map(str::chars)
5682 .count()
5683 + row_delta as usize;
5684 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5685 IndentSize::spaces(chars_to_next_tab_stop)
5686 };
5687 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5688 selection.end = selection.start;
5689 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5690 row_delta += tab_size.len;
5691 }
5692
5693 self.transact(cx, |this, cx| {
5694 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5695 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5696 this.refresh_inline_completion(true, false, cx);
5697 });
5698 }
5699
5700 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5701 if self.read_only(cx) {
5702 return;
5703 }
5704 let mut selections = self.selections.all::<Point>(cx);
5705 let mut prev_edited_row = 0;
5706 let mut row_delta = 0;
5707 let mut edits = Vec::new();
5708 let buffer = self.buffer.read(cx);
5709 let snapshot = buffer.snapshot(cx);
5710 for selection in &mut selections {
5711 if selection.start.row != prev_edited_row {
5712 row_delta = 0;
5713 }
5714 prev_edited_row = selection.end.row;
5715
5716 row_delta =
5717 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5718 }
5719
5720 self.transact(cx, |this, cx| {
5721 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5722 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5723 });
5724 }
5725
5726 fn indent_selection(
5727 buffer: &MultiBuffer,
5728 snapshot: &MultiBufferSnapshot,
5729 selection: &mut Selection<Point>,
5730 edits: &mut Vec<(Range<Point>, String)>,
5731 delta_for_start_row: u32,
5732 cx: &AppContext,
5733 ) -> u32 {
5734 let settings = buffer.settings_at(selection.start, cx);
5735 let tab_size = settings.tab_size.get();
5736 let indent_kind = if settings.hard_tabs {
5737 IndentKind::Tab
5738 } else {
5739 IndentKind::Space
5740 };
5741 let mut start_row = selection.start.row;
5742 let mut end_row = selection.end.row + 1;
5743
5744 // If a selection ends at the beginning of a line, don't indent
5745 // that last line.
5746 if selection.end.column == 0 && selection.end.row > selection.start.row {
5747 end_row -= 1;
5748 }
5749
5750 // Avoid re-indenting a row that has already been indented by a
5751 // previous selection, but still update this selection's column
5752 // to reflect that indentation.
5753 if delta_for_start_row > 0 {
5754 start_row += 1;
5755 selection.start.column += delta_for_start_row;
5756 if selection.end.row == selection.start.row {
5757 selection.end.column += delta_for_start_row;
5758 }
5759 }
5760
5761 let mut delta_for_end_row = 0;
5762 let has_multiple_rows = start_row + 1 != end_row;
5763 for row in start_row..end_row {
5764 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5765 let indent_delta = match (current_indent.kind, indent_kind) {
5766 (IndentKind::Space, IndentKind::Space) => {
5767 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5768 IndentSize::spaces(columns_to_next_tab_stop)
5769 }
5770 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5771 (_, IndentKind::Tab) => IndentSize::tab(),
5772 };
5773
5774 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5775 0
5776 } else {
5777 selection.start.column
5778 };
5779 let row_start = Point::new(row, start);
5780 edits.push((
5781 row_start..row_start,
5782 indent_delta.chars().collect::<String>(),
5783 ));
5784
5785 // Update this selection's endpoints to reflect the indentation.
5786 if row == selection.start.row {
5787 selection.start.column += indent_delta.len;
5788 }
5789 if row == selection.end.row {
5790 selection.end.column += indent_delta.len;
5791 delta_for_end_row = indent_delta.len;
5792 }
5793 }
5794
5795 if selection.start.row == selection.end.row {
5796 delta_for_start_row + delta_for_end_row
5797 } else {
5798 delta_for_end_row
5799 }
5800 }
5801
5802 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5803 if self.read_only(cx) {
5804 return;
5805 }
5806 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5807 let selections = self.selections.all::<Point>(cx);
5808 let mut deletion_ranges = Vec::new();
5809 let mut last_outdent = None;
5810 {
5811 let buffer = self.buffer.read(cx);
5812 let snapshot = buffer.snapshot(cx);
5813 for selection in &selections {
5814 let settings = buffer.settings_at(selection.start, cx);
5815 let tab_size = settings.tab_size.get();
5816 let mut rows = selection.spanned_rows(false, &display_map);
5817
5818 // Avoid re-outdenting a row that has already been outdented by a
5819 // previous selection.
5820 if let Some(last_row) = last_outdent {
5821 if last_row == rows.start {
5822 rows.start = rows.start.next_row();
5823 }
5824 }
5825 let has_multiple_rows = rows.len() > 1;
5826 for row in rows.iter_rows() {
5827 let indent_size = snapshot.indent_size_for_line(row);
5828 if indent_size.len > 0 {
5829 let deletion_len = match indent_size.kind {
5830 IndentKind::Space => {
5831 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5832 if columns_to_prev_tab_stop == 0 {
5833 tab_size
5834 } else {
5835 columns_to_prev_tab_stop
5836 }
5837 }
5838 IndentKind::Tab => 1,
5839 };
5840 let start = if has_multiple_rows
5841 || deletion_len > selection.start.column
5842 || indent_size.len < selection.start.column
5843 {
5844 0
5845 } else {
5846 selection.start.column - deletion_len
5847 };
5848 deletion_ranges.push(
5849 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5850 );
5851 last_outdent = Some(row);
5852 }
5853 }
5854 }
5855 }
5856
5857 self.transact(cx, |this, cx| {
5858 this.buffer.update(cx, |buffer, cx| {
5859 let empty_str: Arc<str> = Arc::default();
5860 buffer.edit(
5861 deletion_ranges
5862 .into_iter()
5863 .map(|range| (range, empty_str.clone())),
5864 None,
5865 cx,
5866 );
5867 });
5868 let selections = this.selections.all::<usize>(cx);
5869 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5870 });
5871 }
5872
5873 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5874 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5875 let selections = self.selections.all::<Point>(cx);
5876
5877 let mut new_cursors = Vec::new();
5878 let mut edit_ranges = Vec::new();
5879 let mut selections = selections.iter().peekable();
5880 while let Some(selection) = selections.next() {
5881 let mut rows = selection.spanned_rows(false, &display_map);
5882 let goal_display_column = selection.head().to_display_point(&display_map).column();
5883
5884 // Accumulate contiguous regions of rows that we want to delete.
5885 while let Some(next_selection) = selections.peek() {
5886 let next_rows = next_selection.spanned_rows(false, &display_map);
5887 if next_rows.start <= rows.end {
5888 rows.end = next_rows.end;
5889 selections.next().unwrap();
5890 } else {
5891 break;
5892 }
5893 }
5894
5895 let buffer = &display_map.buffer_snapshot;
5896 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5897 let edit_end;
5898 let cursor_buffer_row;
5899 if buffer.max_point().row >= rows.end.0 {
5900 // If there's a line after the range, delete the \n from the end of the row range
5901 // and position the cursor on the next line.
5902 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5903 cursor_buffer_row = rows.end;
5904 } else {
5905 // If there isn't a line after the range, delete the \n from the line before the
5906 // start of the row range and position the cursor there.
5907 edit_start = edit_start.saturating_sub(1);
5908 edit_end = buffer.len();
5909 cursor_buffer_row = rows.start.previous_row();
5910 }
5911
5912 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5913 *cursor.column_mut() =
5914 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5915
5916 new_cursors.push((
5917 selection.id,
5918 buffer.anchor_after(cursor.to_point(&display_map)),
5919 ));
5920 edit_ranges.push(edit_start..edit_end);
5921 }
5922
5923 self.transact(cx, |this, cx| {
5924 let buffer = this.buffer.update(cx, |buffer, cx| {
5925 let empty_str: Arc<str> = Arc::default();
5926 buffer.edit(
5927 edit_ranges
5928 .into_iter()
5929 .map(|range| (range, empty_str.clone())),
5930 None,
5931 cx,
5932 );
5933 buffer.snapshot(cx)
5934 });
5935 let new_selections = new_cursors
5936 .into_iter()
5937 .map(|(id, cursor)| {
5938 let cursor = cursor.to_point(&buffer);
5939 Selection {
5940 id,
5941 start: cursor,
5942 end: cursor,
5943 reversed: false,
5944 goal: SelectionGoal::None,
5945 }
5946 })
5947 .collect();
5948
5949 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5950 s.select(new_selections);
5951 });
5952 });
5953 }
5954
5955 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
5956 if self.read_only(cx) {
5957 return;
5958 }
5959 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5960 for selection in self.selections.all::<Point>(cx) {
5961 let start = MultiBufferRow(selection.start.row);
5962 let end = if selection.start.row == selection.end.row {
5963 MultiBufferRow(selection.start.row + 1)
5964 } else {
5965 MultiBufferRow(selection.end.row)
5966 };
5967
5968 if let Some(last_row_range) = row_ranges.last_mut() {
5969 if start <= last_row_range.end {
5970 last_row_range.end = end;
5971 continue;
5972 }
5973 }
5974 row_ranges.push(start..end);
5975 }
5976
5977 let snapshot = self.buffer.read(cx).snapshot(cx);
5978 let mut cursor_positions = Vec::new();
5979 for row_range in &row_ranges {
5980 let anchor = snapshot.anchor_before(Point::new(
5981 row_range.end.previous_row().0,
5982 snapshot.line_len(row_range.end.previous_row()),
5983 ));
5984 cursor_positions.push(anchor..anchor);
5985 }
5986
5987 self.transact(cx, |this, cx| {
5988 for row_range in row_ranges.into_iter().rev() {
5989 for row in row_range.iter_rows().rev() {
5990 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5991 let next_line_row = row.next_row();
5992 let indent = snapshot.indent_size_for_line(next_line_row);
5993 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5994
5995 let replace = if snapshot.line_len(next_line_row) > indent.len {
5996 " "
5997 } else {
5998 ""
5999 };
6000
6001 this.buffer.update(cx, |buffer, cx| {
6002 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6003 });
6004 }
6005 }
6006
6007 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6008 s.select_anchor_ranges(cursor_positions)
6009 });
6010 });
6011 }
6012
6013 pub fn sort_lines_case_sensitive(
6014 &mut self,
6015 _: &SortLinesCaseSensitive,
6016 cx: &mut ViewContext<Self>,
6017 ) {
6018 self.manipulate_lines(cx, |lines| lines.sort())
6019 }
6020
6021 pub fn sort_lines_case_insensitive(
6022 &mut self,
6023 _: &SortLinesCaseInsensitive,
6024 cx: &mut ViewContext<Self>,
6025 ) {
6026 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6027 }
6028
6029 pub fn unique_lines_case_insensitive(
6030 &mut self,
6031 _: &UniqueLinesCaseInsensitive,
6032 cx: &mut ViewContext<Self>,
6033 ) {
6034 self.manipulate_lines(cx, |lines| {
6035 let mut seen = HashSet::default();
6036 lines.retain(|line| seen.insert(line.to_lowercase()));
6037 })
6038 }
6039
6040 pub fn unique_lines_case_sensitive(
6041 &mut self,
6042 _: &UniqueLinesCaseSensitive,
6043 cx: &mut ViewContext<Self>,
6044 ) {
6045 self.manipulate_lines(cx, |lines| {
6046 let mut seen = HashSet::default();
6047 lines.retain(|line| seen.insert(*line));
6048 })
6049 }
6050
6051 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6052 let mut revert_changes = HashMap::default();
6053 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6054 for hunk in hunks_for_rows(
6055 Some(MultiBufferRow(0)..multi_buffer_snapshot.max_buffer_row()).into_iter(),
6056 &multi_buffer_snapshot,
6057 ) {
6058 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
6059 }
6060 if !revert_changes.is_empty() {
6061 self.transact(cx, |editor, cx| {
6062 editor.revert(revert_changes, cx);
6063 });
6064 }
6065 }
6066
6067 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6068 let revert_changes = self.gather_revert_changes(&self.selections.disjoint_anchors(), cx);
6069 if !revert_changes.is_empty() {
6070 self.transact(cx, |editor, cx| {
6071 editor.revert(revert_changes, cx);
6072 });
6073 }
6074 }
6075
6076 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6077 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6078 let project_path = buffer.read(cx).project_path(cx)?;
6079 let project = self.project.as_ref()?.read(cx);
6080 let entry = project.entry_for_path(&project_path, cx)?;
6081 let abs_path = project.absolute_path(&project_path, cx)?;
6082 let parent = if entry.is_symlink {
6083 abs_path.canonicalize().ok()?
6084 } else {
6085 abs_path
6086 }
6087 .parent()?
6088 .to_path_buf();
6089 Some(parent)
6090 }) {
6091 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6092 }
6093 }
6094
6095 fn gather_revert_changes(
6096 &mut self,
6097 selections: &[Selection<Anchor>],
6098 cx: &mut ViewContext<'_, Editor>,
6099 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6100 let mut revert_changes = HashMap::default();
6101 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
6102 for hunk in hunks_for_selections(&multi_buffer_snapshot, selections) {
6103 Self::prepare_revert_change(&mut revert_changes, self.buffer(), &hunk, cx);
6104 }
6105 revert_changes
6106 }
6107
6108 pub fn prepare_revert_change(
6109 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6110 multi_buffer: &Model<MultiBuffer>,
6111 hunk: &DiffHunk<MultiBufferRow>,
6112 cx: &AppContext,
6113 ) -> Option<()> {
6114 let buffer = multi_buffer.read(cx).buffer(hunk.buffer_id)?;
6115 let buffer = buffer.read(cx);
6116 let original_text = buffer.diff_base()?.slice(hunk.diff_base_byte_range.clone());
6117 let buffer_snapshot = buffer.snapshot();
6118 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6119 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6120 probe
6121 .0
6122 .start
6123 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6124 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6125 }) {
6126 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6127 Some(())
6128 } else {
6129 None
6130 }
6131 }
6132
6133 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6134 self.manipulate_lines(cx, |lines| lines.reverse())
6135 }
6136
6137 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6138 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6139 }
6140
6141 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6142 where
6143 Fn: FnMut(&mut Vec<&str>),
6144 {
6145 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6146 let buffer = self.buffer.read(cx).snapshot(cx);
6147
6148 let mut edits = Vec::new();
6149
6150 let selections = self.selections.all::<Point>(cx);
6151 let mut selections = selections.iter().peekable();
6152 let mut contiguous_row_selections = Vec::new();
6153 let mut new_selections = Vec::new();
6154 let mut added_lines = 0;
6155 let mut removed_lines = 0;
6156
6157 while let Some(selection) = selections.next() {
6158 let (start_row, end_row) = consume_contiguous_rows(
6159 &mut contiguous_row_selections,
6160 selection,
6161 &display_map,
6162 &mut selections,
6163 );
6164
6165 let start_point = Point::new(start_row.0, 0);
6166 let end_point = Point::new(
6167 end_row.previous_row().0,
6168 buffer.line_len(end_row.previous_row()),
6169 );
6170 let text = buffer
6171 .text_for_range(start_point..end_point)
6172 .collect::<String>();
6173
6174 let mut lines = text.split('\n').collect_vec();
6175
6176 let lines_before = lines.len();
6177 callback(&mut lines);
6178 let lines_after = lines.len();
6179
6180 edits.push((start_point..end_point, lines.join("\n")));
6181
6182 // Selections must change based on added and removed line count
6183 let start_row =
6184 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6185 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6186 new_selections.push(Selection {
6187 id: selection.id,
6188 start: start_row,
6189 end: end_row,
6190 goal: SelectionGoal::None,
6191 reversed: selection.reversed,
6192 });
6193
6194 if lines_after > lines_before {
6195 added_lines += lines_after - lines_before;
6196 } else if lines_before > lines_after {
6197 removed_lines += lines_before - lines_after;
6198 }
6199 }
6200
6201 self.transact(cx, |this, cx| {
6202 let buffer = this.buffer.update(cx, |buffer, cx| {
6203 buffer.edit(edits, None, cx);
6204 buffer.snapshot(cx)
6205 });
6206
6207 // Recalculate offsets on newly edited buffer
6208 let new_selections = new_selections
6209 .iter()
6210 .map(|s| {
6211 let start_point = Point::new(s.start.0, 0);
6212 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6213 Selection {
6214 id: s.id,
6215 start: buffer.point_to_offset(start_point),
6216 end: buffer.point_to_offset(end_point),
6217 goal: s.goal,
6218 reversed: s.reversed,
6219 }
6220 })
6221 .collect();
6222
6223 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6224 s.select(new_selections);
6225 });
6226
6227 this.request_autoscroll(Autoscroll::fit(), cx);
6228 });
6229 }
6230
6231 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6232 self.manipulate_text(cx, |text| text.to_uppercase())
6233 }
6234
6235 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6236 self.manipulate_text(cx, |text| text.to_lowercase())
6237 }
6238
6239 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6240 self.manipulate_text(cx, |text| {
6241 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6242 // https://github.com/rutrum/convert-case/issues/16
6243 text.split('\n')
6244 .map(|line| line.to_case(Case::Title))
6245 .join("\n")
6246 })
6247 }
6248
6249 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6250 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6251 }
6252
6253 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6254 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6255 }
6256
6257 pub fn convert_to_upper_camel_case(
6258 &mut self,
6259 _: &ConvertToUpperCamelCase,
6260 cx: &mut ViewContext<Self>,
6261 ) {
6262 self.manipulate_text(cx, |text| {
6263 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6264 // https://github.com/rutrum/convert-case/issues/16
6265 text.split('\n')
6266 .map(|line| line.to_case(Case::UpperCamel))
6267 .join("\n")
6268 })
6269 }
6270
6271 pub fn convert_to_lower_camel_case(
6272 &mut self,
6273 _: &ConvertToLowerCamelCase,
6274 cx: &mut ViewContext<Self>,
6275 ) {
6276 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6277 }
6278
6279 pub fn convert_to_opposite_case(
6280 &mut self,
6281 _: &ConvertToOppositeCase,
6282 cx: &mut ViewContext<Self>,
6283 ) {
6284 self.manipulate_text(cx, |text| {
6285 text.chars()
6286 .fold(String::with_capacity(text.len()), |mut t, c| {
6287 if c.is_uppercase() {
6288 t.extend(c.to_lowercase());
6289 } else {
6290 t.extend(c.to_uppercase());
6291 }
6292 t
6293 })
6294 })
6295 }
6296
6297 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6298 where
6299 Fn: FnMut(&str) -> String,
6300 {
6301 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6302 let buffer = self.buffer.read(cx).snapshot(cx);
6303
6304 let mut new_selections = Vec::new();
6305 let mut edits = Vec::new();
6306 let mut selection_adjustment = 0i32;
6307
6308 for selection in self.selections.all::<usize>(cx) {
6309 let selection_is_empty = selection.is_empty();
6310
6311 let (start, end) = if selection_is_empty {
6312 let word_range = movement::surrounding_word(
6313 &display_map,
6314 selection.start.to_display_point(&display_map),
6315 );
6316 let start = word_range.start.to_offset(&display_map, Bias::Left);
6317 let end = word_range.end.to_offset(&display_map, Bias::Left);
6318 (start, end)
6319 } else {
6320 (selection.start, selection.end)
6321 };
6322
6323 let text = buffer.text_for_range(start..end).collect::<String>();
6324 let old_length = text.len() as i32;
6325 let text = callback(&text);
6326
6327 new_selections.push(Selection {
6328 start: (start as i32 - selection_adjustment) as usize,
6329 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6330 goal: SelectionGoal::None,
6331 ..selection
6332 });
6333
6334 selection_adjustment += old_length - text.len() as i32;
6335
6336 edits.push((start..end, text));
6337 }
6338
6339 self.transact(cx, |this, cx| {
6340 this.buffer.update(cx, |buffer, cx| {
6341 buffer.edit(edits, None, cx);
6342 });
6343
6344 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6345 s.select(new_selections);
6346 });
6347
6348 this.request_autoscroll(Autoscroll::fit(), cx);
6349 });
6350 }
6351
6352 pub fn duplicate_line(&mut self, upwards: bool, cx: &mut ViewContext<Self>) {
6353 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6354 let buffer = &display_map.buffer_snapshot;
6355 let selections = self.selections.all::<Point>(cx);
6356
6357 let mut edits = Vec::new();
6358 let mut selections_iter = selections.iter().peekable();
6359 while let Some(selection) = selections_iter.next() {
6360 // Avoid duplicating the same lines twice.
6361 let mut rows = selection.spanned_rows(false, &display_map);
6362
6363 while let Some(next_selection) = selections_iter.peek() {
6364 let next_rows = next_selection.spanned_rows(false, &display_map);
6365 if next_rows.start < rows.end {
6366 rows.end = next_rows.end;
6367 selections_iter.next().unwrap();
6368 } else {
6369 break;
6370 }
6371 }
6372
6373 // Copy the text from the selected row region and splice it either at the start
6374 // or end of the region.
6375 let start = Point::new(rows.start.0, 0);
6376 let end = Point::new(
6377 rows.end.previous_row().0,
6378 buffer.line_len(rows.end.previous_row()),
6379 );
6380 let text = buffer
6381 .text_for_range(start..end)
6382 .chain(Some("\n"))
6383 .collect::<String>();
6384 let insert_location = if upwards {
6385 Point::new(rows.end.0, 0)
6386 } else {
6387 start
6388 };
6389 edits.push((insert_location..insert_location, text));
6390 }
6391
6392 self.transact(cx, |this, cx| {
6393 this.buffer.update(cx, |buffer, cx| {
6394 buffer.edit(edits, None, cx);
6395 });
6396
6397 this.request_autoscroll(Autoscroll::fit(), cx);
6398 });
6399 }
6400
6401 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6402 self.duplicate_line(true, cx);
6403 }
6404
6405 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6406 self.duplicate_line(false, cx);
6407 }
6408
6409 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6410 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6411 let buffer = self.buffer.read(cx).snapshot(cx);
6412
6413 let mut edits = Vec::new();
6414 let mut unfold_ranges = Vec::new();
6415 let mut refold_ranges = Vec::new();
6416
6417 let selections = self.selections.all::<Point>(cx);
6418 let mut selections = selections.iter().peekable();
6419 let mut contiguous_row_selections = Vec::new();
6420 let mut new_selections = Vec::new();
6421
6422 while let Some(selection) = selections.next() {
6423 // Find all the selections that span a contiguous row range
6424 let (start_row, end_row) = consume_contiguous_rows(
6425 &mut contiguous_row_selections,
6426 selection,
6427 &display_map,
6428 &mut selections,
6429 );
6430
6431 // Move the text spanned by the row range to be before the line preceding the row range
6432 if start_row.0 > 0 {
6433 let range_to_move = Point::new(
6434 start_row.previous_row().0,
6435 buffer.line_len(start_row.previous_row()),
6436 )
6437 ..Point::new(
6438 end_row.previous_row().0,
6439 buffer.line_len(end_row.previous_row()),
6440 );
6441 let insertion_point = display_map
6442 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6443 .0;
6444
6445 // Don't move lines across excerpts
6446 if buffer
6447 .excerpt_boundaries_in_range((
6448 Bound::Excluded(insertion_point),
6449 Bound::Included(range_to_move.end),
6450 ))
6451 .next()
6452 .is_none()
6453 {
6454 let text = buffer
6455 .text_for_range(range_to_move.clone())
6456 .flat_map(|s| s.chars())
6457 .skip(1)
6458 .chain(['\n'])
6459 .collect::<String>();
6460
6461 edits.push((
6462 buffer.anchor_after(range_to_move.start)
6463 ..buffer.anchor_before(range_to_move.end),
6464 String::new(),
6465 ));
6466 let insertion_anchor = buffer.anchor_after(insertion_point);
6467 edits.push((insertion_anchor..insertion_anchor, text));
6468
6469 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6470
6471 // Move selections up
6472 new_selections.extend(contiguous_row_selections.drain(..).map(
6473 |mut selection| {
6474 selection.start.row -= row_delta;
6475 selection.end.row -= row_delta;
6476 selection
6477 },
6478 ));
6479
6480 // Move folds up
6481 unfold_ranges.push(range_to_move.clone());
6482 for fold in display_map.folds_in_range(
6483 buffer.anchor_before(range_to_move.start)
6484 ..buffer.anchor_after(range_to_move.end),
6485 ) {
6486 let mut start = fold.range.start.to_point(&buffer);
6487 let mut end = fold.range.end.to_point(&buffer);
6488 start.row -= row_delta;
6489 end.row -= row_delta;
6490 refold_ranges.push((start..end, fold.placeholder.clone()));
6491 }
6492 }
6493 }
6494
6495 // If we didn't move line(s), preserve the existing selections
6496 new_selections.append(&mut contiguous_row_selections);
6497 }
6498
6499 self.transact(cx, |this, cx| {
6500 this.unfold_ranges(unfold_ranges, true, true, cx);
6501 this.buffer.update(cx, |buffer, cx| {
6502 for (range, text) in edits {
6503 buffer.edit([(range, text)], None, cx);
6504 }
6505 });
6506 this.fold_ranges(refold_ranges, true, cx);
6507 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6508 s.select(new_selections);
6509 })
6510 });
6511 }
6512
6513 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6514 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6515 let buffer = self.buffer.read(cx).snapshot(cx);
6516
6517 let mut edits = Vec::new();
6518 let mut unfold_ranges = Vec::new();
6519 let mut refold_ranges = Vec::new();
6520
6521 let selections = self.selections.all::<Point>(cx);
6522 let mut selections = selections.iter().peekable();
6523 let mut contiguous_row_selections = Vec::new();
6524 let mut new_selections = Vec::new();
6525
6526 while let Some(selection) = selections.next() {
6527 // Find all the selections that span a contiguous row range
6528 let (start_row, end_row) = consume_contiguous_rows(
6529 &mut contiguous_row_selections,
6530 selection,
6531 &display_map,
6532 &mut selections,
6533 );
6534
6535 // Move the text spanned by the row range to be after the last line of the row range
6536 if end_row.0 <= buffer.max_point().row {
6537 let range_to_move =
6538 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6539 let insertion_point = display_map
6540 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6541 .0;
6542
6543 // Don't move lines across excerpt boundaries
6544 if buffer
6545 .excerpt_boundaries_in_range((
6546 Bound::Excluded(range_to_move.start),
6547 Bound::Included(insertion_point),
6548 ))
6549 .next()
6550 .is_none()
6551 {
6552 let mut text = String::from("\n");
6553 text.extend(buffer.text_for_range(range_to_move.clone()));
6554 text.pop(); // Drop trailing newline
6555 edits.push((
6556 buffer.anchor_after(range_to_move.start)
6557 ..buffer.anchor_before(range_to_move.end),
6558 String::new(),
6559 ));
6560 let insertion_anchor = buffer.anchor_after(insertion_point);
6561 edits.push((insertion_anchor..insertion_anchor, text));
6562
6563 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6564
6565 // Move selections down
6566 new_selections.extend(contiguous_row_selections.drain(..).map(
6567 |mut selection| {
6568 selection.start.row += row_delta;
6569 selection.end.row += row_delta;
6570 selection
6571 },
6572 ));
6573
6574 // Move folds down
6575 unfold_ranges.push(range_to_move.clone());
6576 for fold in display_map.folds_in_range(
6577 buffer.anchor_before(range_to_move.start)
6578 ..buffer.anchor_after(range_to_move.end),
6579 ) {
6580 let mut start = fold.range.start.to_point(&buffer);
6581 let mut end = fold.range.end.to_point(&buffer);
6582 start.row += row_delta;
6583 end.row += row_delta;
6584 refold_ranges.push((start..end, fold.placeholder.clone()));
6585 }
6586 }
6587 }
6588
6589 // If we didn't move line(s), preserve the existing selections
6590 new_selections.append(&mut contiguous_row_selections);
6591 }
6592
6593 self.transact(cx, |this, cx| {
6594 this.unfold_ranges(unfold_ranges, true, true, cx);
6595 this.buffer.update(cx, |buffer, cx| {
6596 for (range, text) in edits {
6597 buffer.edit([(range, text)], None, cx);
6598 }
6599 });
6600 this.fold_ranges(refold_ranges, true, cx);
6601 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6602 });
6603 }
6604
6605 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6606 let text_layout_details = &self.text_layout_details(cx);
6607 self.transact(cx, |this, cx| {
6608 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6609 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6610 let line_mode = s.line_mode;
6611 s.move_with(|display_map, selection| {
6612 if !selection.is_empty() || line_mode {
6613 return;
6614 }
6615
6616 let mut head = selection.head();
6617 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6618 if head.column() == display_map.line_len(head.row()) {
6619 transpose_offset = display_map
6620 .buffer_snapshot
6621 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6622 }
6623
6624 if transpose_offset == 0 {
6625 return;
6626 }
6627
6628 *head.column_mut() += 1;
6629 head = display_map.clip_point(head, Bias::Right);
6630 let goal = SelectionGoal::HorizontalPosition(
6631 display_map
6632 .x_for_display_point(head, text_layout_details)
6633 .into(),
6634 );
6635 selection.collapse_to(head, goal);
6636
6637 let transpose_start = display_map
6638 .buffer_snapshot
6639 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6640 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6641 let transpose_end = display_map
6642 .buffer_snapshot
6643 .clip_offset(transpose_offset + 1, Bias::Right);
6644 if let Some(ch) =
6645 display_map.buffer_snapshot.chars_at(transpose_start).next()
6646 {
6647 edits.push((transpose_start..transpose_offset, String::new()));
6648 edits.push((transpose_end..transpose_end, ch.to_string()));
6649 }
6650 }
6651 });
6652 edits
6653 });
6654 this.buffer
6655 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6656 let selections = this.selections.all::<usize>(cx);
6657 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6658 s.select(selections);
6659 });
6660 });
6661 }
6662
6663 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6664 let buffer = self.buffer.read(cx).snapshot(cx);
6665 let selections = self.selections.all::<Point>(cx);
6666 let mut selections = selections.iter().peekable();
6667
6668 let mut edits = Vec::new();
6669 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
6670
6671 while let Some(selection) = selections.next() {
6672 let mut start_row = selection.start.row;
6673 let mut end_row = selection.end.row;
6674
6675 // Skip selections that overlap with a range that has already been rewrapped.
6676 let selection_range = start_row..end_row;
6677 if rewrapped_row_ranges
6678 .iter()
6679 .any(|range| range.overlaps(&selection_range))
6680 {
6681 continue;
6682 }
6683
6684 let mut should_rewrap = false;
6685
6686 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
6687 match language_scope.language_name().0.as_ref() {
6688 "Markdown" | "Plain Text" => {
6689 should_rewrap = true;
6690 }
6691 _ => {}
6692 }
6693 }
6694
6695 let row = selection.head().row;
6696 let indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
6697 let indent_end = Point::new(row, indent_size.len);
6698
6699 let mut line_prefix = indent_size.chars().collect::<String>();
6700
6701 if selection.is_empty() {
6702 if let Some(comment_prefix) =
6703 buffer
6704 .language_scope_at(selection.head())
6705 .and_then(|language| {
6706 language
6707 .line_comment_prefixes()
6708 .iter()
6709 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
6710 .cloned()
6711 })
6712 {
6713 line_prefix.push_str(&comment_prefix);
6714 should_rewrap = true;
6715 }
6716
6717 'expand_upwards: while start_row > 0 {
6718 let prev_row = start_row - 1;
6719 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
6720 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
6721 {
6722 start_row = prev_row;
6723 } else {
6724 break 'expand_upwards;
6725 }
6726 }
6727
6728 'expand_downwards: while end_row < buffer.max_point().row {
6729 let next_row = end_row + 1;
6730 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
6731 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
6732 {
6733 end_row = next_row;
6734 } else {
6735 break 'expand_downwards;
6736 }
6737 }
6738 }
6739
6740 if !should_rewrap {
6741 continue;
6742 }
6743
6744 let start = Point::new(start_row, 0);
6745 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
6746 let selection_text = buffer.text_for_range(start..end).collect::<String>();
6747 let unwrapped_text = selection_text
6748 .lines()
6749 .map(|line| line.strip_prefix(&line_prefix).unwrap())
6750 .join(" ");
6751 let wrap_column = buffer
6752 .settings_at(Point::new(start_row, 0), cx)
6753 .preferred_line_length as usize;
6754 let mut wrapped_text = String::new();
6755 let mut current_line = line_prefix.clone();
6756 for word in unwrapped_text.split_whitespace() {
6757 if current_line.len() + word.len() >= wrap_column {
6758 wrapped_text.push_str(¤t_line);
6759 wrapped_text.push('\n');
6760 current_line.truncate(line_prefix.len());
6761 }
6762
6763 if current_line.len() > line_prefix.len() {
6764 current_line.push(' ');
6765 }
6766
6767 current_line.push_str(word);
6768 }
6769
6770 if !current_line.is_empty() {
6771 wrapped_text.push_str(¤t_line);
6772 }
6773
6774 let diff = TextDiff::from_lines(&selection_text, &wrapped_text);
6775 let mut offset = start.to_offset(&buffer);
6776 let mut moved_since_edit = true;
6777
6778 for change in diff.iter_all_changes() {
6779 let value = change.value();
6780 match change.tag() {
6781 ChangeTag::Equal => {
6782 offset += value.len();
6783 moved_since_edit = true;
6784 }
6785 ChangeTag::Delete => {
6786 let start = buffer.anchor_after(offset);
6787 let end = buffer.anchor_before(offset + value.len());
6788
6789 if moved_since_edit {
6790 edits.push((start..end, String::new()));
6791 } else {
6792 edits.last_mut().unwrap().0.end = end;
6793 }
6794
6795 offset += value.len();
6796 moved_since_edit = false;
6797 }
6798 ChangeTag::Insert => {
6799 if moved_since_edit {
6800 let anchor = buffer.anchor_after(offset);
6801 edits.push((anchor..anchor, value.to_string()));
6802 } else {
6803 edits.last_mut().unwrap().1.push_str(value);
6804 }
6805
6806 moved_since_edit = false;
6807 }
6808 }
6809 }
6810
6811 rewrapped_row_ranges.push(start_row..=end_row);
6812 }
6813
6814 self.buffer
6815 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6816 }
6817
6818 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6819 let mut text = String::new();
6820 let buffer = self.buffer.read(cx).snapshot(cx);
6821 let mut selections = self.selections.all::<Point>(cx);
6822 let mut clipboard_selections = Vec::with_capacity(selections.len());
6823 {
6824 let max_point = buffer.max_point();
6825 let mut is_first = true;
6826 for selection in &mut selections {
6827 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6828 if is_entire_line {
6829 selection.start = Point::new(selection.start.row, 0);
6830 if !selection.is_empty() && selection.end.column == 0 {
6831 selection.end = cmp::min(max_point, selection.end);
6832 } else {
6833 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6834 }
6835 selection.goal = SelectionGoal::None;
6836 }
6837 if is_first {
6838 is_first = false;
6839 } else {
6840 text += "\n";
6841 }
6842 let mut len = 0;
6843 for chunk in buffer.text_for_range(selection.start..selection.end) {
6844 text.push_str(chunk);
6845 len += chunk.len();
6846 }
6847 clipboard_selections.push(ClipboardSelection {
6848 len,
6849 is_entire_line,
6850 first_line_indent: buffer
6851 .indent_size_for_line(MultiBufferRow(selection.start.row))
6852 .len,
6853 });
6854 }
6855 }
6856
6857 self.transact(cx, |this, cx| {
6858 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6859 s.select(selections);
6860 });
6861 this.insert("", cx);
6862 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6863 text,
6864 clipboard_selections,
6865 ));
6866 });
6867 }
6868
6869 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6870 let selections = self.selections.all::<Point>(cx);
6871 let buffer = self.buffer.read(cx).read(cx);
6872 let mut text = String::new();
6873
6874 let mut clipboard_selections = Vec::with_capacity(selections.len());
6875 {
6876 let max_point = buffer.max_point();
6877 let mut is_first = true;
6878 for selection in selections.iter() {
6879 let mut start = selection.start;
6880 let mut end = selection.end;
6881 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6882 if is_entire_line {
6883 start = Point::new(start.row, 0);
6884 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6885 }
6886 if is_first {
6887 is_first = false;
6888 } else {
6889 text += "\n";
6890 }
6891 let mut len = 0;
6892 for chunk in buffer.text_for_range(start..end) {
6893 text.push_str(chunk);
6894 len += chunk.len();
6895 }
6896 clipboard_selections.push(ClipboardSelection {
6897 len,
6898 is_entire_line,
6899 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6900 });
6901 }
6902 }
6903
6904 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6905 text,
6906 clipboard_selections,
6907 ));
6908 }
6909
6910 pub fn do_paste(
6911 &mut self,
6912 text: &String,
6913 clipboard_selections: Option<Vec<ClipboardSelection>>,
6914 handle_entire_lines: bool,
6915 cx: &mut ViewContext<Self>,
6916 ) {
6917 if self.read_only(cx) {
6918 return;
6919 }
6920
6921 let clipboard_text = Cow::Borrowed(text);
6922
6923 self.transact(cx, |this, cx| {
6924 if let Some(mut clipboard_selections) = clipboard_selections {
6925 let old_selections = this.selections.all::<usize>(cx);
6926 let all_selections_were_entire_line =
6927 clipboard_selections.iter().all(|s| s.is_entire_line);
6928 let first_selection_indent_column =
6929 clipboard_selections.first().map(|s| s.first_line_indent);
6930 if clipboard_selections.len() != old_selections.len() {
6931 clipboard_selections.drain(..);
6932 }
6933
6934 this.buffer.update(cx, |buffer, cx| {
6935 let snapshot = buffer.read(cx);
6936 let mut start_offset = 0;
6937 let mut edits = Vec::new();
6938 let mut original_indent_columns = Vec::new();
6939 for (ix, selection) in old_selections.iter().enumerate() {
6940 let to_insert;
6941 let entire_line;
6942 let original_indent_column;
6943 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
6944 let end_offset = start_offset + clipboard_selection.len;
6945 to_insert = &clipboard_text[start_offset..end_offset];
6946 entire_line = clipboard_selection.is_entire_line;
6947 start_offset = end_offset + 1;
6948 original_indent_column = Some(clipboard_selection.first_line_indent);
6949 } else {
6950 to_insert = clipboard_text.as_str();
6951 entire_line = all_selections_were_entire_line;
6952 original_indent_column = first_selection_indent_column
6953 }
6954
6955 // If the corresponding selection was empty when this slice of the
6956 // clipboard text was written, then the entire line containing the
6957 // selection was copied. If this selection is also currently empty,
6958 // then paste the line before the current line of the buffer.
6959 let range = if selection.is_empty() && handle_entire_lines && entire_line {
6960 let column = selection.start.to_point(&snapshot).column as usize;
6961 let line_start = selection.start - column;
6962 line_start..line_start
6963 } else {
6964 selection.range()
6965 };
6966
6967 edits.push((range, to_insert));
6968 original_indent_columns.extend(original_indent_column);
6969 }
6970 drop(snapshot);
6971
6972 buffer.edit(
6973 edits,
6974 Some(AutoindentMode::Block {
6975 original_indent_columns,
6976 }),
6977 cx,
6978 );
6979 });
6980
6981 let selections = this.selections.all::<usize>(cx);
6982 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6983 } else {
6984 this.insert(&clipboard_text, cx);
6985 }
6986 });
6987 }
6988
6989 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
6990 if let Some(item) = cx.read_from_clipboard() {
6991 let entries = item.entries();
6992
6993 match entries.first() {
6994 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
6995 // of all the pasted entries.
6996 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
6997 .do_paste(
6998 clipboard_string.text(),
6999 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7000 true,
7001 cx,
7002 ),
7003 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
7004 }
7005 }
7006 }
7007
7008 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
7009 if self.read_only(cx) {
7010 return;
7011 }
7012
7013 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7014 if let Some((selections, _)) =
7015 self.selection_history.transaction(transaction_id).cloned()
7016 {
7017 self.change_selections(None, cx, |s| {
7018 s.select_anchors(selections.to_vec());
7019 });
7020 }
7021 self.request_autoscroll(Autoscroll::fit(), cx);
7022 self.unmark_text(cx);
7023 self.refresh_inline_completion(true, false, cx);
7024 cx.emit(EditorEvent::Edited { transaction_id });
7025 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7026 }
7027 }
7028
7029 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
7030 if self.read_only(cx) {
7031 return;
7032 }
7033
7034 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7035 if let Some((_, Some(selections))) =
7036 self.selection_history.transaction(transaction_id).cloned()
7037 {
7038 self.change_selections(None, cx, |s| {
7039 s.select_anchors(selections.to_vec());
7040 });
7041 }
7042 self.request_autoscroll(Autoscroll::fit(), cx);
7043 self.unmark_text(cx);
7044 self.refresh_inline_completion(true, false, cx);
7045 cx.emit(EditorEvent::Edited { transaction_id });
7046 }
7047 }
7048
7049 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
7050 self.buffer
7051 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7052 }
7053
7054 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
7055 self.buffer
7056 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7057 }
7058
7059 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
7060 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7061 let line_mode = s.line_mode;
7062 s.move_with(|map, selection| {
7063 let cursor = if selection.is_empty() && !line_mode {
7064 movement::left(map, selection.start)
7065 } else {
7066 selection.start
7067 };
7068 selection.collapse_to(cursor, SelectionGoal::None);
7069 });
7070 })
7071 }
7072
7073 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
7074 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7075 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7076 })
7077 }
7078
7079 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
7080 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7081 let line_mode = s.line_mode;
7082 s.move_with(|map, selection| {
7083 let cursor = if selection.is_empty() && !line_mode {
7084 movement::right(map, selection.end)
7085 } else {
7086 selection.end
7087 };
7088 selection.collapse_to(cursor, SelectionGoal::None)
7089 });
7090 })
7091 }
7092
7093 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
7094 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7095 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7096 })
7097 }
7098
7099 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
7100 if self.take_rename(true, cx).is_some() {
7101 return;
7102 }
7103
7104 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7105 cx.propagate();
7106 return;
7107 }
7108
7109 let text_layout_details = &self.text_layout_details(cx);
7110 let selection_count = self.selections.count();
7111 let first_selection = self.selections.first_anchor();
7112
7113 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7114 let line_mode = s.line_mode;
7115 s.move_with(|map, selection| {
7116 if !selection.is_empty() && !line_mode {
7117 selection.goal = SelectionGoal::None;
7118 }
7119 let (cursor, goal) = movement::up(
7120 map,
7121 selection.start,
7122 selection.goal,
7123 false,
7124 text_layout_details,
7125 );
7126 selection.collapse_to(cursor, goal);
7127 });
7128 });
7129
7130 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7131 {
7132 cx.propagate();
7133 }
7134 }
7135
7136 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7137 if self.take_rename(true, cx).is_some() {
7138 return;
7139 }
7140
7141 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7142 cx.propagate();
7143 return;
7144 }
7145
7146 let text_layout_details = &self.text_layout_details(cx);
7147
7148 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7149 let line_mode = s.line_mode;
7150 s.move_with(|map, selection| {
7151 if !selection.is_empty() && !line_mode {
7152 selection.goal = SelectionGoal::None;
7153 }
7154 let (cursor, goal) = movement::up_by_rows(
7155 map,
7156 selection.start,
7157 action.lines,
7158 selection.goal,
7159 false,
7160 text_layout_details,
7161 );
7162 selection.collapse_to(cursor, goal);
7163 });
7164 })
7165 }
7166
7167 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7168 if self.take_rename(true, cx).is_some() {
7169 return;
7170 }
7171
7172 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7173 cx.propagate();
7174 return;
7175 }
7176
7177 let text_layout_details = &self.text_layout_details(cx);
7178
7179 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7180 let line_mode = s.line_mode;
7181 s.move_with(|map, selection| {
7182 if !selection.is_empty() && !line_mode {
7183 selection.goal = SelectionGoal::None;
7184 }
7185 let (cursor, goal) = movement::down_by_rows(
7186 map,
7187 selection.start,
7188 action.lines,
7189 selection.goal,
7190 false,
7191 text_layout_details,
7192 );
7193 selection.collapse_to(cursor, goal);
7194 });
7195 })
7196 }
7197
7198 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7199 let text_layout_details = &self.text_layout_details(cx);
7200 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7201 s.move_heads_with(|map, head, goal| {
7202 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7203 })
7204 })
7205 }
7206
7207 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7208 let text_layout_details = &self.text_layout_details(cx);
7209 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7210 s.move_heads_with(|map, head, goal| {
7211 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7212 })
7213 })
7214 }
7215
7216 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7217 let Some(row_count) = self.visible_row_count() else {
7218 return;
7219 };
7220
7221 let text_layout_details = &self.text_layout_details(cx);
7222
7223 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7224 s.move_heads_with(|map, head, goal| {
7225 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7226 })
7227 })
7228 }
7229
7230 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7231 if self.take_rename(true, cx).is_some() {
7232 return;
7233 }
7234
7235 if self
7236 .context_menu
7237 .write()
7238 .as_mut()
7239 .map(|menu| menu.select_first(self.project.as_ref(), cx))
7240 .unwrap_or(false)
7241 {
7242 return;
7243 }
7244
7245 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7246 cx.propagate();
7247 return;
7248 }
7249
7250 let Some(row_count) = self.visible_row_count() else {
7251 return;
7252 };
7253
7254 let autoscroll = if action.center_cursor {
7255 Autoscroll::center()
7256 } else {
7257 Autoscroll::fit()
7258 };
7259
7260 let text_layout_details = &self.text_layout_details(cx);
7261
7262 self.change_selections(Some(autoscroll), cx, |s| {
7263 let line_mode = s.line_mode;
7264 s.move_with(|map, selection| {
7265 if !selection.is_empty() && !line_mode {
7266 selection.goal = SelectionGoal::None;
7267 }
7268 let (cursor, goal) = movement::up_by_rows(
7269 map,
7270 selection.end,
7271 row_count,
7272 selection.goal,
7273 false,
7274 text_layout_details,
7275 );
7276 selection.collapse_to(cursor, goal);
7277 });
7278 });
7279 }
7280
7281 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7282 let text_layout_details = &self.text_layout_details(cx);
7283 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7284 s.move_heads_with(|map, head, goal| {
7285 movement::up(map, head, goal, false, text_layout_details)
7286 })
7287 })
7288 }
7289
7290 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7291 self.take_rename(true, cx);
7292
7293 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7294 cx.propagate();
7295 return;
7296 }
7297
7298 let text_layout_details = &self.text_layout_details(cx);
7299 let selection_count = self.selections.count();
7300 let first_selection = self.selections.first_anchor();
7301
7302 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7303 let line_mode = s.line_mode;
7304 s.move_with(|map, selection| {
7305 if !selection.is_empty() && !line_mode {
7306 selection.goal = SelectionGoal::None;
7307 }
7308 let (cursor, goal) = movement::down(
7309 map,
7310 selection.end,
7311 selection.goal,
7312 false,
7313 text_layout_details,
7314 );
7315 selection.collapse_to(cursor, goal);
7316 });
7317 });
7318
7319 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7320 {
7321 cx.propagate();
7322 }
7323 }
7324
7325 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7326 let Some(row_count) = self.visible_row_count() else {
7327 return;
7328 };
7329
7330 let text_layout_details = &self.text_layout_details(cx);
7331
7332 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7333 s.move_heads_with(|map, head, goal| {
7334 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7335 })
7336 })
7337 }
7338
7339 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7340 if self.take_rename(true, cx).is_some() {
7341 return;
7342 }
7343
7344 if self
7345 .context_menu
7346 .write()
7347 .as_mut()
7348 .map(|menu| menu.select_last(self.project.as_ref(), cx))
7349 .unwrap_or(false)
7350 {
7351 return;
7352 }
7353
7354 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7355 cx.propagate();
7356 return;
7357 }
7358
7359 let Some(row_count) = self.visible_row_count() else {
7360 return;
7361 };
7362
7363 let autoscroll = if action.center_cursor {
7364 Autoscroll::center()
7365 } else {
7366 Autoscroll::fit()
7367 };
7368
7369 let text_layout_details = &self.text_layout_details(cx);
7370 self.change_selections(Some(autoscroll), cx, |s| {
7371 let line_mode = s.line_mode;
7372 s.move_with(|map, selection| {
7373 if !selection.is_empty() && !line_mode {
7374 selection.goal = SelectionGoal::None;
7375 }
7376 let (cursor, goal) = movement::down_by_rows(
7377 map,
7378 selection.end,
7379 row_count,
7380 selection.goal,
7381 false,
7382 text_layout_details,
7383 );
7384 selection.collapse_to(cursor, goal);
7385 });
7386 });
7387 }
7388
7389 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7390 let text_layout_details = &self.text_layout_details(cx);
7391 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7392 s.move_heads_with(|map, head, goal| {
7393 movement::down(map, head, goal, false, text_layout_details)
7394 })
7395 });
7396 }
7397
7398 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7399 if let Some(context_menu) = self.context_menu.write().as_mut() {
7400 context_menu.select_first(self.project.as_ref(), cx);
7401 }
7402 }
7403
7404 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7405 if let Some(context_menu) = self.context_menu.write().as_mut() {
7406 context_menu.select_prev(self.project.as_ref(), cx);
7407 }
7408 }
7409
7410 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7411 if let Some(context_menu) = self.context_menu.write().as_mut() {
7412 context_menu.select_next(self.project.as_ref(), cx);
7413 }
7414 }
7415
7416 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7417 if let Some(context_menu) = self.context_menu.write().as_mut() {
7418 context_menu.select_last(self.project.as_ref(), cx);
7419 }
7420 }
7421
7422 pub fn move_to_previous_word_start(
7423 &mut self,
7424 _: &MoveToPreviousWordStart,
7425 cx: &mut ViewContext<Self>,
7426 ) {
7427 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7428 s.move_cursors_with(|map, head, _| {
7429 (
7430 movement::previous_word_start(map, head),
7431 SelectionGoal::None,
7432 )
7433 });
7434 })
7435 }
7436
7437 pub fn move_to_previous_subword_start(
7438 &mut self,
7439 _: &MoveToPreviousSubwordStart,
7440 cx: &mut ViewContext<Self>,
7441 ) {
7442 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7443 s.move_cursors_with(|map, head, _| {
7444 (
7445 movement::previous_subword_start(map, head),
7446 SelectionGoal::None,
7447 )
7448 });
7449 })
7450 }
7451
7452 pub fn select_to_previous_word_start(
7453 &mut self,
7454 _: &SelectToPreviousWordStart,
7455 cx: &mut ViewContext<Self>,
7456 ) {
7457 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7458 s.move_heads_with(|map, head, _| {
7459 (
7460 movement::previous_word_start(map, head),
7461 SelectionGoal::None,
7462 )
7463 });
7464 })
7465 }
7466
7467 pub fn select_to_previous_subword_start(
7468 &mut self,
7469 _: &SelectToPreviousSubwordStart,
7470 cx: &mut ViewContext<Self>,
7471 ) {
7472 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7473 s.move_heads_with(|map, head, _| {
7474 (
7475 movement::previous_subword_start(map, head),
7476 SelectionGoal::None,
7477 )
7478 });
7479 })
7480 }
7481
7482 pub fn delete_to_previous_word_start(
7483 &mut self,
7484 action: &DeleteToPreviousWordStart,
7485 cx: &mut ViewContext<Self>,
7486 ) {
7487 self.transact(cx, |this, cx| {
7488 this.select_autoclose_pair(cx);
7489 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7490 let line_mode = s.line_mode;
7491 s.move_with(|map, selection| {
7492 if selection.is_empty() && !line_mode {
7493 let cursor = if action.ignore_newlines {
7494 movement::previous_word_start(map, selection.head())
7495 } else {
7496 movement::previous_word_start_or_newline(map, selection.head())
7497 };
7498 selection.set_head(cursor, SelectionGoal::None);
7499 }
7500 });
7501 });
7502 this.insert("", cx);
7503 });
7504 }
7505
7506 pub fn delete_to_previous_subword_start(
7507 &mut self,
7508 _: &DeleteToPreviousSubwordStart,
7509 cx: &mut ViewContext<Self>,
7510 ) {
7511 self.transact(cx, |this, cx| {
7512 this.select_autoclose_pair(cx);
7513 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7514 let line_mode = s.line_mode;
7515 s.move_with(|map, selection| {
7516 if selection.is_empty() && !line_mode {
7517 let cursor = movement::previous_subword_start(map, selection.head());
7518 selection.set_head(cursor, SelectionGoal::None);
7519 }
7520 });
7521 });
7522 this.insert("", cx);
7523 });
7524 }
7525
7526 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7527 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7528 s.move_cursors_with(|map, head, _| {
7529 (movement::next_word_end(map, head), SelectionGoal::None)
7530 });
7531 })
7532 }
7533
7534 pub fn move_to_next_subword_end(
7535 &mut self,
7536 _: &MoveToNextSubwordEnd,
7537 cx: &mut ViewContext<Self>,
7538 ) {
7539 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7540 s.move_cursors_with(|map, head, _| {
7541 (movement::next_subword_end(map, head), SelectionGoal::None)
7542 });
7543 })
7544 }
7545
7546 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7547 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7548 s.move_heads_with(|map, head, _| {
7549 (movement::next_word_end(map, head), SelectionGoal::None)
7550 });
7551 })
7552 }
7553
7554 pub fn select_to_next_subword_end(
7555 &mut self,
7556 _: &SelectToNextSubwordEnd,
7557 cx: &mut ViewContext<Self>,
7558 ) {
7559 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7560 s.move_heads_with(|map, head, _| {
7561 (movement::next_subword_end(map, head), SelectionGoal::None)
7562 });
7563 })
7564 }
7565
7566 pub fn delete_to_next_word_end(
7567 &mut self,
7568 action: &DeleteToNextWordEnd,
7569 cx: &mut ViewContext<Self>,
7570 ) {
7571 self.transact(cx, |this, cx| {
7572 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7573 let line_mode = s.line_mode;
7574 s.move_with(|map, selection| {
7575 if selection.is_empty() && !line_mode {
7576 let cursor = if action.ignore_newlines {
7577 movement::next_word_end(map, selection.head())
7578 } else {
7579 movement::next_word_end_or_newline(map, selection.head())
7580 };
7581 selection.set_head(cursor, SelectionGoal::None);
7582 }
7583 });
7584 });
7585 this.insert("", cx);
7586 });
7587 }
7588
7589 pub fn delete_to_next_subword_end(
7590 &mut self,
7591 _: &DeleteToNextSubwordEnd,
7592 cx: &mut ViewContext<Self>,
7593 ) {
7594 self.transact(cx, |this, cx| {
7595 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7596 s.move_with(|map, selection| {
7597 if selection.is_empty() {
7598 let cursor = movement::next_subword_end(map, selection.head());
7599 selection.set_head(cursor, SelectionGoal::None);
7600 }
7601 });
7602 });
7603 this.insert("", cx);
7604 });
7605 }
7606
7607 pub fn move_to_beginning_of_line(
7608 &mut self,
7609 action: &MoveToBeginningOfLine,
7610 cx: &mut ViewContext<Self>,
7611 ) {
7612 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7613 s.move_cursors_with(|map, head, _| {
7614 (
7615 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7616 SelectionGoal::None,
7617 )
7618 });
7619 })
7620 }
7621
7622 pub fn select_to_beginning_of_line(
7623 &mut self,
7624 action: &SelectToBeginningOfLine,
7625 cx: &mut ViewContext<Self>,
7626 ) {
7627 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7628 s.move_heads_with(|map, head, _| {
7629 (
7630 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7631 SelectionGoal::None,
7632 )
7633 });
7634 });
7635 }
7636
7637 pub fn delete_to_beginning_of_line(
7638 &mut self,
7639 _: &DeleteToBeginningOfLine,
7640 cx: &mut ViewContext<Self>,
7641 ) {
7642 self.transact(cx, |this, cx| {
7643 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7644 s.move_with(|_, selection| {
7645 selection.reversed = true;
7646 });
7647 });
7648
7649 this.select_to_beginning_of_line(
7650 &SelectToBeginningOfLine {
7651 stop_at_soft_wraps: false,
7652 },
7653 cx,
7654 );
7655 this.backspace(&Backspace, cx);
7656 });
7657 }
7658
7659 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7660 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7661 s.move_cursors_with(|map, head, _| {
7662 (
7663 movement::line_end(map, head, action.stop_at_soft_wraps),
7664 SelectionGoal::None,
7665 )
7666 });
7667 })
7668 }
7669
7670 pub fn select_to_end_of_line(
7671 &mut self,
7672 action: &SelectToEndOfLine,
7673 cx: &mut ViewContext<Self>,
7674 ) {
7675 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7676 s.move_heads_with(|map, head, _| {
7677 (
7678 movement::line_end(map, head, action.stop_at_soft_wraps),
7679 SelectionGoal::None,
7680 )
7681 });
7682 })
7683 }
7684
7685 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7686 self.transact(cx, |this, cx| {
7687 this.select_to_end_of_line(
7688 &SelectToEndOfLine {
7689 stop_at_soft_wraps: false,
7690 },
7691 cx,
7692 );
7693 this.delete(&Delete, cx);
7694 });
7695 }
7696
7697 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7698 self.transact(cx, |this, cx| {
7699 this.select_to_end_of_line(
7700 &SelectToEndOfLine {
7701 stop_at_soft_wraps: false,
7702 },
7703 cx,
7704 );
7705 this.cut(&Cut, cx);
7706 });
7707 }
7708
7709 pub fn move_to_start_of_paragraph(
7710 &mut self,
7711 _: &MoveToStartOfParagraph,
7712 cx: &mut ViewContext<Self>,
7713 ) {
7714 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7715 cx.propagate();
7716 return;
7717 }
7718
7719 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7720 s.move_with(|map, selection| {
7721 selection.collapse_to(
7722 movement::start_of_paragraph(map, selection.head(), 1),
7723 SelectionGoal::None,
7724 )
7725 });
7726 })
7727 }
7728
7729 pub fn move_to_end_of_paragraph(
7730 &mut self,
7731 _: &MoveToEndOfParagraph,
7732 cx: &mut ViewContext<Self>,
7733 ) {
7734 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7735 cx.propagate();
7736 return;
7737 }
7738
7739 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7740 s.move_with(|map, selection| {
7741 selection.collapse_to(
7742 movement::end_of_paragraph(map, selection.head(), 1),
7743 SelectionGoal::None,
7744 )
7745 });
7746 })
7747 }
7748
7749 pub fn select_to_start_of_paragraph(
7750 &mut self,
7751 _: &SelectToStartOfParagraph,
7752 cx: &mut ViewContext<Self>,
7753 ) {
7754 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7755 cx.propagate();
7756 return;
7757 }
7758
7759 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7760 s.move_heads_with(|map, head, _| {
7761 (
7762 movement::start_of_paragraph(map, head, 1),
7763 SelectionGoal::None,
7764 )
7765 });
7766 })
7767 }
7768
7769 pub fn select_to_end_of_paragraph(
7770 &mut self,
7771 _: &SelectToEndOfParagraph,
7772 cx: &mut ViewContext<Self>,
7773 ) {
7774 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7775 cx.propagate();
7776 return;
7777 }
7778
7779 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7780 s.move_heads_with(|map, head, _| {
7781 (
7782 movement::end_of_paragraph(map, head, 1),
7783 SelectionGoal::None,
7784 )
7785 });
7786 })
7787 }
7788
7789 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7790 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7791 cx.propagate();
7792 return;
7793 }
7794
7795 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7796 s.select_ranges(vec![0..0]);
7797 });
7798 }
7799
7800 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7801 let mut selection = self.selections.last::<Point>(cx);
7802 selection.set_head(Point::zero(), SelectionGoal::None);
7803
7804 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7805 s.select(vec![selection]);
7806 });
7807 }
7808
7809 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7810 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7811 cx.propagate();
7812 return;
7813 }
7814
7815 let cursor = self.buffer.read(cx).read(cx).len();
7816 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7817 s.select_ranges(vec![cursor..cursor])
7818 });
7819 }
7820
7821 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7822 self.nav_history = nav_history;
7823 }
7824
7825 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7826 self.nav_history.as_ref()
7827 }
7828
7829 fn push_to_nav_history(
7830 &mut self,
7831 cursor_anchor: Anchor,
7832 new_position: Option<Point>,
7833 cx: &mut ViewContext<Self>,
7834 ) {
7835 if let Some(nav_history) = self.nav_history.as_mut() {
7836 let buffer = self.buffer.read(cx).read(cx);
7837 let cursor_position = cursor_anchor.to_point(&buffer);
7838 let scroll_state = self.scroll_manager.anchor();
7839 let scroll_top_row = scroll_state.top_row(&buffer);
7840 drop(buffer);
7841
7842 if let Some(new_position) = new_position {
7843 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7844 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7845 return;
7846 }
7847 }
7848
7849 nav_history.push(
7850 Some(NavigationData {
7851 cursor_anchor,
7852 cursor_position,
7853 scroll_anchor: scroll_state,
7854 scroll_top_row,
7855 }),
7856 cx,
7857 );
7858 }
7859 }
7860
7861 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7862 let buffer = self.buffer.read(cx).snapshot(cx);
7863 let mut selection = self.selections.first::<usize>(cx);
7864 selection.set_head(buffer.len(), SelectionGoal::None);
7865 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7866 s.select(vec![selection]);
7867 });
7868 }
7869
7870 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7871 let end = self.buffer.read(cx).read(cx).len();
7872 self.change_selections(None, cx, |s| {
7873 s.select_ranges(vec![0..end]);
7874 });
7875 }
7876
7877 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7878 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7879 let mut selections = self.selections.all::<Point>(cx);
7880 let max_point = display_map.buffer_snapshot.max_point();
7881 for selection in &mut selections {
7882 let rows = selection.spanned_rows(true, &display_map);
7883 selection.start = Point::new(rows.start.0, 0);
7884 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7885 selection.reversed = false;
7886 }
7887 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7888 s.select(selections);
7889 });
7890 }
7891
7892 pub fn split_selection_into_lines(
7893 &mut self,
7894 _: &SplitSelectionIntoLines,
7895 cx: &mut ViewContext<Self>,
7896 ) {
7897 let mut to_unfold = Vec::new();
7898 let mut new_selection_ranges = Vec::new();
7899 {
7900 let selections = self.selections.all::<Point>(cx);
7901 let buffer = self.buffer.read(cx).read(cx);
7902 for selection in selections {
7903 for row in selection.start.row..selection.end.row {
7904 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
7905 new_selection_ranges.push(cursor..cursor);
7906 }
7907 new_selection_ranges.push(selection.end..selection.end);
7908 to_unfold.push(selection.start..selection.end);
7909 }
7910 }
7911 self.unfold_ranges(to_unfold, true, true, cx);
7912 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7913 s.select_ranges(new_selection_ranges);
7914 });
7915 }
7916
7917 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
7918 self.add_selection(true, cx);
7919 }
7920
7921 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
7922 self.add_selection(false, cx);
7923 }
7924
7925 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
7926 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7927 let mut selections = self.selections.all::<Point>(cx);
7928 let text_layout_details = self.text_layout_details(cx);
7929 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
7930 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
7931 let range = oldest_selection.display_range(&display_map).sorted();
7932
7933 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
7934 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
7935 let positions = start_x.min(end_x)..start_x.max(end_x);
7936
7937 selections.clear();
7938 let mut stack = Vec::new();
7939 for row in range.start.row().0..=range.end.row().0 {
7940 if let Some(selection) = self.selections.build_columnar_selection(
7941 &display_map,
7942 DisplayRow(row),
7943 &positions,
7944 oldest_selection.reversed,
7945 &text_layout_details,
7946 ) {
7947 stack.push(selection.id);
7948 selections.push(selection);
7949 }
7950 }
7951
7952 if above {
7953 stack.reverse();
7954 }
7955
7956 AddSelectionsState { above, stack }
7957 });
7958
7959 let last_added_selection = *state.stack.last().unwrap();
7960 let mut new_selections = Vec::new();
7961 if above == state.above {
7962 let end_row = if above {
7963 DisplayRow(0)
7964 } else {
7965 display_map.max_point().row()
7966 };
7967
7968 'outer: for selection in selections {
7969 if selection.id == last_added_selection {
7970 let range = selection.display_range(&display_map).sorted();
7971 debug_assert_eq!(range.start.row(), range.end.row());
7972 let mut row = range.start.row();
7973 let positions =
7974 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
7975 px(start)..px(end)
7976 } else {
7977 let start_x =
7978 display_map.x_for_display_point(range.start, &text_layout_details);
7979 let end_x =
7980 display_map.x_for_display_point(range.end, &text_layout_details);
7981 start_x.min(end_x)..start_x.max(end_x)
7982 };
7983
7984 while row != end_row {
7985 if above {
7986 row.0 -= 1;
7987 } else {
7988 row.0 += 1;
7989 }
7990
7991 if let Some(new_selection) = self.selections.build_columnar_selection(
7992 &display_map,
7993 row,
7994 &positions,
7995 selection.reversed,
7996 &text_layout_details,
7997 ) {
7998 state.stack.push(new_selection.id);
7999 if above {
8000 new_selections.push(new_selection);
8001 new_selections.push(selection);
8002 } else {
8003 new_selections.push(selection);
8004 new_selections.push(new_selection);
8005 }
8006
8007 continue 'outer;
8008 }
8009 }
8010 }
8011
8012 new_selections.push(selection);
8013 }
8014 } else {
8015 new_selections = selections;
8016 new_selections.retain(|s| s.id != last_added_selection);
8017 state.stack.pop();
8018 }
8019
8020 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8021 s.select(new_selections);
8022 });
8023 if state.stack.len() > 1 {
8024 self.add_selections_state = Some(state);
8025 }
8026 }
8027
8028 pub fn select_next_match_internal(
8029 &mut self,
8030 display_map: &DisplaySnapshot,
8031 replace_newest: bool,
8032 autoscroll: Option<Autoscroll>,
8033 cx: &mut ViewContext<Self>,
8034 ) -> Result<()> {
8035 fn select_next_match_ranges(
8036 this: &mut Editor,
8037 range: Range<usize>,
8038 replace_newest: bool,
8039 auto_scroll: Option<Autoscroll>,
8040 cx: &mut ViewContext<Editor>,
8041 ) {
8042 this.unfold_ranges([range.clone()], false, true, cx);
8043 this.change_selections(auto_scroll, cx, |s| {
8044 if replace_newest {
8045 s.delete(s.newest_anchor().id);
8046 }
8047 s.insert_range(range.clone());
8048 });
8049 }
8050
8051 let buffer = &display_map.buffer_snapshot;
8052 let mut selections = self.selections.all::<usize>(cx);
8053 if let Some(mut select_next_state) = self.select_next_state.take() {
8054 let query = &select_next_state.query;
8055 if !select_next_state.done {
8056 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8057 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8058 let mut next_selected_range = None;
8059
8060 let bytes_after_last_selection =
8061 buffer.bytes_in_range(last_selection.end..buffer.len());
8062 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8063 let query_matches = query
8064 .stream_find_iter(bytes_after_last_selection)
8065 .map(|result| (last_selection.end, result))
8066 .chain(
8067 query
8068 .stream_find_iter(bytes_before_first_selection)
8069 .map(|result| (0, result)),
8070 );
8071
8072 for (start_offset, query_match) in query_matches {
8073 let query_match = query_match.unwrap(); // can only fail due to I/O
8074 let offset_range =
8075 start_offset + query_match.start()..start_offset + query_match.end();
8076 let display_range = offset_range.start.to_display_point(display_map)
8077 ..offset_range.end.to_display_point(display_map);
8078
8079 if !select_next_state.wordwise
8080 || (!movement::is_inside_word(display_map, display_range.start)
8081 && !movement::is_inside_word(display_map, display_range.end))
8082 {
8083 // TODO: This is n^2, because we might check all the selections
8084 if !selections
8085 .iter()
8086 .any(|selection| selection.range().overlaps(&offset_range))
8087 {
8088 next_selected_range = Some(offset_range);
8089 break;
8090 }
8091 }
8092 }
8093
8094 if let Some(next_selected_range) = next_selected_range {
8095 select_next_match_ranges(
8096 self,
8097 next_selected_range,
8098 replace_newest,
8099 autoscroll,
8100 cx,
8101 );
8102 } else {
8103 select_next_state.done = true;
8104 }
8105 }
8106
8107 self.select_next_state = Some(select_next_state);
8108 } else {
8109 let mut only_carets = true;
8110 let mut same_text_selected = true;
8111 let mut selected_text = None;
8112
8113 let mut selections_iter = selections.iter().peekable();
8114 while let Some(selection) = selections_iter.next() {
8115 if selection.start != selection.end {
8116 only_carets = false;
8117 }
8118
8119 if same_text_selected {
8120 if selected_text.is_none() {
8121 selected_text =
8122 Some(buffer.text_for_range(selection.range()).collect::<String>());
8123 }
8124
8125 if let Some(next_selection) = selections_iter.peek() {
8126 if next_selection.range().len() == selection.range().len() {
8127 let next_selected_text = buffer
8128 .text_for_range(next_selection.range())
8129 .collect::<String>();
8130 if Some(next_selected_text) != selected_text {
8131 same_text_selected = false;
8132 selected_text = None;
8133 }
8134 } else {
8135 same_text_selected = false;
8136 selected_text = None;
8137 }
8138 }
8139 }
8140 }
8141
8142 if only_carets {
8143 for selection in &mut selections {
8144 let word_range = movement::surrounding_word(
8145 display_map,
8146 selection.start.to_display_point(display_map),
8147 );
8148 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8149 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8150 selection.goal = SelectionGoal::None;
8151 selection.reversed = false;
8152 select_next_match_ranges(
8153 self,
8154 selection.start..selection.end,
8155 replace_newest,
8156 autoscroll,
8157 cx,
8158 );
8159 }
8160
8161 if selections.len() == 1 {
8162 let selection = selections
8163 .last()
8164 .expect("ensured that there's only one selection");
8165 let query = buffer
8166 .text_for_range(selection.start..selection.end)
8167 .collect::<String>();
8168 let is_empty = query.is_empty();
8169 let select_state = SelectNextState {
8170 query: AhoCorasick::new(&[query])?,
8171 wordwise: true,
8172 done: is_empty,
8173 };
8174 self.select_next_state = Some(select_state);
8175 } else {
8176 self.select_next_state = None;
8177 }
8178 } else if let Some(selected_text) = selected_text {
8179 self.select_next_state = Some(SelectNextState {
8180 query: AhoCorasick::new(&[selected_text])?,
8181 wordwise: false,
8182 done: false,
8183 });
8184 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8185 }
8186 }
8187 Ok(())
8188 }
8189
8190 pub fn select_all_matches(
8191 &mut self,
8192 _action: &SelectAllMatches,
8193 cx: &mut ViewContext<Self>,
8194 ) -> Result<()> {
8195 self.push_to_selection_history();
8196 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8197
8198 self.select_next_match_internal(&display_map, false, None, cx)?;
8199 let Some(select_next_state) = self.select_next_state.as_mut() else {
8200 return Ok(());
8201 };
8202 if select_next_state.done {
8203 return Ok(());
8204 }
8205
8206 let mut new_selections = self.selections.all::<usize>(cx);
8207
8208 let buffer = &display_map.buffer_snapshot;
8209 let query_matches = select_next_state
8210 .query
8211 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8212
8213 for query_match in query_matches {
8214 let query_match = query_match.unwrap(); // can only fail due to I/O
8215 let offset_range = query_match.start()..query_match.end();
8216 let display_range = offset_range.start.to_display_point(&display_map)
8217 ..offset_range.end.to_display_point(&display_map);
8218
8219 if !select_next_state.wordwise
8220 || (!movement::is_inside_word(&display_map, display_range.start)
8221 && !movement::is_inside_word(&display_map, display_range.end))
8222 {
8223 self.selections.change_with(cx, |selections| {
8224 new_selections.push(Selection {
8225 id: selections.new_selection_id(),
8226 start: offset_range.start,
8227 end: offset_range.end,
8228 reversed: false,
8229 goal: SelectionGoal::None,
8230 });
8231 });
8232 }
8233 }
8234
8235 new_selections.sort_by_key(|selection| selection.start);
8236 let mut ix = 0;
8237 while ix + 1 < new_selections.len() {
8238 let current_selection = &new_selections[ix];
8239 let next_selection = &new_selections[ix + 1];
8240 if current_selection.range().overlaps(&next_selection.range()) {
8241 if current_selection.id < next_selection.id {
8242 new_selections.remove(ix + 1);
8243 } else {
8244 new_selections.remove(ix);
8245 }
8246 } else {
8247 ix += 1;
8248 }
8249 }
8250
8251 select_next_state.done = true;
8252 self.unfold_ranges(
8253 new_selections.iter().map(|selection| selection.range()),
8254 false,
8255 false,
8256 cx,
8257 );
8258 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8259 selections.select(new_selections)
8260 });
8261
8262 Ok(())
8263 }
8264
8265 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8266 self.push_to_selection_history();
8267 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8268 self.select_next_match_internal(
8269 &display_map,
8270 action.replace_newest,
8271 Some(Autoscroll::newest()),
8272 cx,
8273 )?;
8274 Ok(())
8275 }
8276
8277 pub fn select_previous(
8278 &mut self,
8279 action: &SelectPrevious,
8280 cx: &mut ViewContext<Self>,
8281 ) -> Result<()> {
8282 self.push_to_selection_history();
8283 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8284 let buffer = &display_map.buffer_snapshot;
8285 let mut selections = self.selections.all::<usize>(cx);
8286 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8287 let query = &select_prev_state.query;
8288 if !select_prev_state.done {
8289 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8290 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8291 let mut next_selected_range = None;
8292 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8293 let bytes_before_last_selection =
8294 buffer.reversed_bytes_in_range(0..last_selection.start);
8295 let bytes_after_first_selection =
8296 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8297 let query_matches = query
8298 .stream_find_iter(bytes_before_last_selection)
8299 .map(|result| (last_selection.start, result))
8300 .chain(
8301 query
8302 .stream_find_iter(bytes_after_first_selection)
8303 .map(|result| (buffer.len(), result)),
8304 );
8305 for (end_offset, query_match) in query_matches {
8306 let query_match = query_match.unwrap(); // can only fail due to I/O
8307 let offset_range =
8308 end_offset - query_match.end()..end_offset - query_match.start();
8309 let display_range = offset_range.start.to_display_point(&display_map)
8310 ..offset_range.end.to_display_point(&display_map);
8311
8312 if !select_prev_state.wordwise
8313 || (!movement::is_inside_word(&display_map, display_range.start)
8314 && !movement::is_inside_word(&display_map, display_range.end))
8315 {
8316 next_selected_range = Some(offset_range);
8317 break;
8318 }
8319 }
8320
8321 if let Some(next_selected_range) = next_selected_range {
8322 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
8323 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8324 if action.replace_newest {
8325 s.delete(s.newest_anchor().id);
8326 }
8327 s.insert_range(next_selected_range);
8328 });
8329 } else {
8330 select_prev_state.done = true;
8331 }
8332 }
8333
8334 self.select_prev_state = Some(select_prev_state);
8335 } else {
8336 let mut only_carets = true;
8337 let mut same_text_selected = true;
8338 let mut selected_text = None;
8339
8340 let mut selections_iter = selections.iter().peekable();
8341 while let Some(selection) = selections_iter.next() {
8342 if selection.start != selection.end {
8343 only_carets = false;
8344 }
8345
8346 if same_text_selected {
8347 if selected_text.is_none() {
8348 selected_text =
8349 Some(buffer.text_for_range(selection.range()).collect::<String>());
8350 }
8351
8352 if let Some(next_selection) = selections_iter.peek() {
8353 if next_selection.range().len() == selection.range().len() {
8354 let next_selected_text = buffer
8355 .text_for_range(next_selection.range())
8356 .collect::<String>();
8357 if Some(next_selected_text) != selected_text {
8358 same_text_selected = false;
8359 selected_text = None;
8360 }
8361 } else {
8362 same_text_selected = false;
8363 selected_text = None;
8364 }
8365 }
8366 }
8367 }
8368
8369 if only_carets {
8370 for selection in &mut selections {
8371 let word_range = movement::surrounding_word(
8372 &display_map,
8373 selection.start.to_display_point(&display_map),
8374 );
8375 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8376 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8377 selection.goal = SelectionGoal::None;
8378 selection.reversed = false;
8379 }
8380 if selections.len() == 1 {
8381 let selection = selections
8382 .last()
8383 .expect("ensured that there's only one selection");
8384 let query = buffer
8385 .text_for_range(selection.start..selection.end)
8386 .collect::<String>();
8387 let is_empty = query.is_empty();
8388 let select_state = SelectNextState {
8389 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8390 wordwise: true,
8391 done: is_empty,
8392 };
8393 self.select_prev_state = Some(select_state);
8394 } else {
8395 self.select_prev_state = None;
8396 }
8397
8398 self.unfold_ranges(
8399 selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8400 false,
8401 true,
8402 cx,
8403 );
8404 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8405 s.select(selections);
8406 });
8407 } else if let Some(selected_text) = selected_text {
8408 self.select_prev_state = Some(SelectNextState {
8409 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8410 wordwise: false,
8411 done: false,
8412 });
8413 self.select_previous(action, cx)?;
8414 }
8415 }
8416 Ok(())
8417 }
8418
8419 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8420 let text_layout_details = &self.text_layout_details(cx);
8421 self.transact(cx, |this, cx| {
8422 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8423 let mut edits = Vec::new();
8424 let mut selection_edit_ranges = Vec::new();
8425 let mut last_toggled_row = None;
8426 let snapshot = this.buffer.read(cx).read(cx);
8427 let empty_str: Arc<str> = Arc::default();
8428 let mut suffixes_inserted = Vec::new();
8429
8430 fn comment_prefix_range(
8431 snapshot: &MultiBufferSnapshot,
8432 row: MultiBufferRow,
8433 comment_prefix: &str,
8434 comment_prefix_whitespace: &str,
8435 ) -> Range<Point> {
8436 let start = Point::new(row.0, snapshot.indent_size_for_line(row).len);
8437
8438 let mut line_bytes = snapshot
8439 .bytes_in_range(start..snapshot.max_point())
8440 .flatten()
8441 .copied();
8442
8443 // If this line currently begins with the line comment prefix, then record
8444 // the range containing the prefix.
8445 if line_bytes
8446 .by_ref()
8447 .take(comment_prefix.len())
8448 .eq(comment_prefix.bytes())
8449 {
8450 // Include any whitespace that matches the comment prefix.
8451 let matching_whitespace_len = line_bytes
8452 .zip(comment_prefix_whitespace.bytes())
8453 .take_while(|(a, b)| a == b)
8454 .count() as u32;
8455 let end = Point::new(
8456 start.row,
8457 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8458 );
8459 start..end
8460 } else {
8461 start..start
8462 }
8463 }
8464
8465 fn comment_suffix_range(
8466 snapshot: &MultiBufferSnapshot,
8467 row: MultiBufferRow,
8468 comment_suffix: &str,
8469 comment_suffix_has_leading_space: bool,
8470 ) -> Range<Point> {
8471 let end = Point::new(row.0, snapshot.line_len(row));
8472 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8473
8474 let mut line_end_bytes = snapshot
8475 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8476 .flatten()
8477 .copied();
8478
8479 let leading_space_len = if suffix_start_column > 0
8480 && line_end_bytes.next() == Some(b' ')
8481 && comment_suffix_has_leading_space
8482 {
8483 1
8484 } else {
8485 0
8486 };
8487
8488 // If this line currently begins with the line comment prefix, then record
8489 // the range containing the prefix.
8490 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8491 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8492 start..end
8493 } else {
8494 end..end
8495 }
8496 }
8497
8498 // TODO: Handle selections that cross excerpts
8499 for selection in &mut selections {
8500 let start_column = snapshot
8501 .indent_size_for_line(MultiBufferRow(selection.start.row))
8502 .len;
8503 let language = if let Some(language) =
8504 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8505 {
8506 language
8507 } else {
8508 continue;
8509 };
8510
8511 selection_edit_ranges.clear();
8512
8513 // If multiple selections contain a given row, avoid processing that
8514 // row more than once.
8515 let mut start_row = MultiBufferRow(selection.start.row);
8516 if last_toggled_row == Some(start_row) {
8517 start_row = start_row.next_row();
8518 }
8519 let end_row =
8520 if selection.end.row > selection.start.row && selection.end.column == 0 {
8521 MultiBufferRow(selection.end.row - 1)
8522 } else {
8523 MultiBufferRow(selection.end.row)
8524 };
8525 last_toggled_row = Some(end_row);
8526
8527 if start_row > end_row {
8528 continue;
8529 }
8530
8531 // If the language has line comments, toggle those.
8532 let full_comment_prefixes = language.line_comment_prefixes();
8533 if !full_comment_prefixes.is_empty() {
8534 let first_prefix = full_comment_prefixes
8535 .first()
8536 .expect("prefixes is non-empty");
8537 let prefix_trimmed_lengths = full_comment_prefixes
8538 .iter()
8539 .map(|p| p.trim_end_matches(' ').len())
8540 .collect::<SmallVec<[usize; 4]>>();
8541
8542 let mut all_selection_lines_are_comments = true;
8543
8544 for row in start_row.0..=end_row.0 {
8545 let row = MultiBufferRow(row);
8546 if start_row < end_row && snapshot.is_line_blank(row) {
8547 continue;
8548 }
8549
8550 let prefix_range = full_comment_prefixes
8551 .iter()
8552 .zip(prefix_trimmed_lengths.iter().copied())
8553 .map(|(prefix, trimmed_prefix_len)| {
8554 comment_prefix_range(
8555 snapshot.deref(),
8556 row,
8557 &prefix[..trimmed_prefix_len],
8558 &prefix[trimmed_prefix_len..],
8559 )
8560 })
8561 .max_by_key(|range| range.end.column - range.start.column)
8562 .expect("prefixes is non-empty");
8563
8564 if prefix_range.is_empty() {
8565 all_selection_lines_are_comments = false;
8566 }
8567
8568 selection_edit_ranges.push(prefix_range);
8569 }
8570
8571 if all_selection_lines_are_comments {
8572 edits.extend(
8573 selection_edit_ranges
8574 .iter()
8575 .cloned()
8576 .map(|range| (range, empty_str.clone())),
8577 );
8578 } else {
8579 let min_column = selection_edit_ranges
8580 .iter()
8581 .map(|range| range.start.column)
8582 .min()
8583 .unwrap_or(0);
8584 edits.extend(selection_edit_ranges.iter().map(|range| {
8585 let position = Point::new(range.start.row, min_column);
8586 (position..position, first_prefix.clone())
8587 }));
8588 }
8589 } else if let Some((full_comment_prefix, comment_suffix)) =
8590 language.block_comment_delimiters()
8591 {
8592 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8593 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8594 let prefix_range = comment_prefix_range(
8595 snapshot.deref(),
8596 start_row,
8597 comment_prefix,
8598 comment_prefix_whitespace,
8599 );
8600 let suffix_range = comment_suffix_range(
8601 snapshot.deref(),
8602 end_row,
8603 comment_suffix.trim_start_matches(' '),
8604 comment_suffix.starts_with(' '),
8605 );
8606
8607 if prefix_range.is_empty() || suffix_range.is_empty() {
8608 edits.push((
8609 prefix_range.start..prefix_range.start,
8610 full_comment_prefix.clone(),
8611 ));
8612 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8613 suffixes_inserted.push((end_row, comment_suffix.len()));
8614 } else {
8615 edits.push((prefix_range, empty_str.clone()));
8616 edits.push((suffix_range, empty_str.clone()));
8617 }
8618 } else {
8619 continue;
8620 }
8621 }
8622
8623 drop(snapshot);
8624 this.buffer.update(cx, |buffer, cx| {
8625 buffer.edit(edits, None, cx);
8626 });
8627
8628 // Adjust selections so that they end before any comment suffixes that
8629 // were inserted.
8630 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8631 let mut selections = this.selections.all::<Point>(cx);
8632 let snapshot = this.buffer.read(cx).read(cx);
8633 for selection in &mut selections {
8634 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8635 match row.cmp(&MultiBufferRow(selection.end.row)) {
8636 Ordering::Less => {
8637 suffixes_inserted.next();
8638 continue;
8639 }
8640 Ordering::Greater => break,
8641 Ordering::Equal => {
8642 if selection.end.column == snapshot.line_len(row) {
8643 if selection.is_empty() {
8644 selection.start.column -= suffix_len as u32;
8645 }
8646 selection.end.column -= suffix_len as u32;
8647 }
8648 break;
8649 }
8650 }
8651 }
8652 }
8653
8654 drop(snapshot);
8655 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8656
8657 let selections = this.selections.all::<Point>(cx);
8658 let selections_on_single_row = selections.windows(2).all(|selections| {
8659 selections[0].start.row == selections[1].start.row
8660 && selections[0].end.row == selections[1].end.row
8661 && selections[0].start.row == selections[0].end.row
8662 });
8663 let selections_selecting = selections
8664 .iter()
8665 .any(|selection| selection.start != selection.end);
8666 let advance_downwards = action.advance_downwards
8667 && selections_on_single_row
8668 && !selections_selecting
8669 && !matches!(this.mode, EditorMode::SingleLine { .. });
8670
8671 if advance_downwards {
8672 let snapshot = this.buffer.read(cx).snapshot(cx);
8673
8674 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8675 s.move_cursors_with(|display_snapshot, display_point, _| {
8676 let mut point = display_point.to_point(display_snapshot);
8677 point.row += 1;
8678 point = snapshot.clip_point(point, Bias::Left);
8679 let display_point = point.to_display_point(display_snapshot);
8680 let goal = SelectionGoal::HorizontalPosition(
8681 display_snapshot
8682 .x_for_display_point(display_point, text_layout_details)
8683 .into(),
8684 );
8685 (display_point, goal)
8686 })
8687 });
8688 }
8689 });
8690 }
8691
8692 pub fn select_enclosing_symbol(
8693 &mut self,
8694 _: &SelectEnclosingSymbol,
8695 cx: &mut ViewContext<Self>,
8696 ) {
8697 let buffer = self.buffer.read(cx).snapshot(cx);
8698 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8699
8700 fn update_selection(
8701 selection: &Selection<usize>,
8702 buffer_snap: &MultiBufferSnapshot,
8703 ) -> Option<Selection<usize>> {
8704 let cursor = selection.head();
8705 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8706 for symbol in symbols.iter().rev() {
8707 let start = symbol.range.start.to_offset(buffer_snap);
8708 let end = symbol.range.end.to_offset(buffer_snap);
8709 let new_range = start..end;
8710 if start < selection.start || end > selection.end {
8711 return Some(Selection {
8712 id: selection.id,
8713 start: new_range.start,
8714 end: new_range.end,
8715 goal: SelectionGoal::None,
8716 reversed: selection.reversed,
8717 });
8718 }
8719 }
8720 None
8721 }
8722
8723 let mut selected_larger_symbol = false;
8724 let new_selections = old_selections
8725 .iter()
8726 .map(|selection| match update_selection(selection, &buffer) {
8727 Some(new_selection) => {
8728 if new_selection.range() != selection.range() {
8729 selected_larger_symbol = true;
8730 }
8731 new_selection
8732 }
8733 None => selection.clone(),
8734 })
8735 .collect::<Vec<_>>();
8736
8737 if selected_larger_symbol {
8738 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8739 s.select(new_selections);
8740 });
8741 }
8742 }
8743
8744 pub fn select_larger_syntax_node(
8745 &mut self,
8746 _: &SelectLargerSyntaxNode,
8747 cx: &mut ViewContext<Self>,
8748 ) {
8749 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8750 let buffer = self.buffer.read(cx).snapshot(cx);
8751 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8752
8753 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8754 let mut selected_larger_node = false;
8755 let new_selections = old_selections
8756 .iter()
8757 .map(|selection| {
8758 let old_range = selection.start..selection.end;
8759 let mut new_range = old_range.clone();
8760 while let Some(containing_range) =
8761 buffer.range_for_syntax_ancestor(new_range.clone())
8762 {
8763 new_range = containing_range;
8764 if !display_map.intersects_fold(new_range.start)
8765 && !display_map.intersects_fold(new_range.end)
8766 {
8767 break;
8768 }
8769 }
8770
8771 selected_larger_node |= new_range != old_range;
8772 Selection {
8773 id: selection.id,
8774 start: new_range.start,
8775 end: new_range.end,
8776 goal: SelectionGoal::None,
8777 reversed: selection.reversed,
8778 }
8779 })
8780 .collect::<Vec<_>>();
8781
8782 if selected_larger_node {
8783 stack.push(old_selections);
8784 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8785 s.select(new_selections);
8786 });
8787 }
8788 self.select_larger_syntax_node_stack = stack;
8789 }
8790
8791 pub fn select_smaller_syntax_node(
8792 &mut self,
8793 _: &SelectSmallerSyntaxNode,
8794 cx: &mut ViewContext<Self>,
8795 ) {
8796 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8797 if let Some(selections) = stack.pop() {
8798 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8799 s.select(selections.to_vec());
8800 });
8801 }
8802 self.select_larger_syntax_node_stack = stack;
8803 }
8804
8805 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8806 if !EditorSettings::get_global(cx).gutter.runnables {
8807 self.clear_tasks();
8808 return Task::ready(());
8809 }
8810 let project = self.project.clone();
8811 cx.spawn(|this, mut cx| async move {
8812 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8813 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8814 }) else {
8815 return;
8816 };
8817
8818 let Some(project) = project else {
8819 return;
8820 };
8821
8822 let hide_runnables = project
8823 .update(&mut cx, |project, cx| {
8824 // Do not display any test indicators in non-dev server remote projects.
8825 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8826 })
8827 .unwrap_or(true);
8828 if hide_runnables {
8829 return;
8830 }
8831 let new_rows =
8832 cx.background_executor()
8833 .spawn({
8834 let snapshot = display_snapshot.clone();
8835 async move {
8836 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8837 }
8838 })
8839 .await;
8840 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8841
8842 this.update(&mut cx, |this, _| {
8843 this.clear_tasks();
8844 for (key, value) in rows {
8845 this.insert_tasks(key, value);
8846 }
8847 })
8848 .ok();
8849 })
8850 }
8851 fn fetch_runnable_ranges(
8852 snapshot: &DisplaySnapshot,
8853 range: Range<Anchor>,
8854 ) -> Vec<language::RunnableRange> {
8855 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8856 }
8857
8858 fn runnable_rows(
8859 project: Model<Project>,
8860 snapshot: DisplaySnapshot,
8861 runnable_ranges: Vec<RunnableRange>,
8862 mut cx: AsyncWindowContext,
8863 ) -> Vec<((BufferId, u32), RunnableTasks)> {
8864 runnable_ranges
8865 .into_iter()
8866 .filter_map(|mut runnable| {
8867 let tasks = cx
8868 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
8869 .ok()?;
8870 if tasks.is_empty() {
8871 return None;
8872 }
8873
8874 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
8875
8876 let row = snapshot
8877 .buffer_snapshot
8878 .buffer_line_for_row(MultiBufferRow(point.row))?
8879 .1
8880 .start
8881 .row;
8882
8883 let context_range =
8884 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
8885 Some((
8886 (runnable.buffer_id, row),
8887 RunnableTasks {
8888 templates: tasks,
8889 offset: MultiBufferOffset(runnable.run_range.start),
8890 context_range,
8891 column: point.column,
8892 extra_variables: runnable.extra_captures,
8893 },
8894 ))
8895 })
8896 .collect()
8897 }
8898
8899 fn templates_with_tags(
8900 project: &Model<Project>,
8901 runnable: &mut Runnable,
8902 cx: &WindowContext<'_>,
8903 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
8904 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
8905 let (worktree_id, file) = project
8906 .buffer_for_id(runnable.buffer, cx)
8907 .and_then(|buffer| buffer.read(cx).file())
8908 .map(|file| (file.worktree_id(cx), file.clone()))
8909 .unzip();
8910
8911 (project.task_inventory().clone(), worktree_id, file)
8912 });
8913
8914 let inventory = inventory.read(cx);
8915 let tags = mem::take(&mut runnable.tags);
8916 let mut tags: Vec<_> = tags
8917 .into_iter()
8918 .flat_map(|tag| {
8919 let tag = tag.0.clone();
8920 inventory
8921 .list_tasks(
8922 file.clone(),
8923 Some(runnable.language.clone()),
8924 worktree_id,
8925 cx,
8926 )
8927 .into_iter()
8928 .filter(move |(_, template)| {
8929 template.tags.iter().any(|source_tag| source_tag == &tag)
8930 })
8931 })
8932 .sorted_by_key(|(kind, _)| kind.to_owned())
8933 .collect();
8934 if let Some((leading_tag_source, _)) = tags.first() {
8935 // Strongest source wins; if we have worktree tag binding, prefer that to
8936 // global and language bindings;
8937 // if we have a global binding, prefer that to language binding.
8938 let first_mismatch = tags
8939 .iter()
8940 .position(|(tag_source, _)| tag_source != leading_tag_source);
8941 if let Some(index) = first_mismatch {
8942 tags.truncate(index);
8943 }
8944 }
8945
8946 tags
8947 }
8948
8949 pub fn move_to_enclosing_bracket(
8950 &mut self,
8951 _: &MoveToEnclosingBracket,
8952 cx: &mut ViewContext<Self>,
8953 ) {
8954 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8955 s.move_offsets_with(|snapshot, selection| {
8956 let Some(enclosing_bracket_ranges) =
8957 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
8958 else {
8959 return;
8960 };
8961
8962 let mut best_length = usize::MAX;
8963 let mut best_inside = false;
8964 let mut best_in_bracket_range = false;
8965 let mut best_destination = None;
8966 for (open, close) in enclosing_bracket_ranges {
8967 let close = close.to_inclusive();
8968 let length = close.end() - open.start;
8969 let inside = selection.start >= open.end && selection.end <= *close.start();
8970 let in_bracket_range = open.to_inclusive().contains(&selection.head())
8971 || close.contains(&selection.head());
8972
8973 // If best is next to a bracket and current isn't, skip
8974 if !in_bracket_range && best_in_bracket_range {
8975 continue;
8976 }
8977
8978 // Prefer smaller lengths unless best is inside and current isn't
8979 if length > best_length && (best_inside || !inside) {
8980 continue;
8981 }
8982
8983 best_length = length;
8984 best_inside = inside;
8985 best_in_bracket_range = in_bracket_range;
8986 best_destination = Some(
8987 if close.contains(&selection.start) && close.contains(&selection.end) {
8988 if inside {
8989 open.end
8990 } else {
8991 open.start
8992 }
8993 } else if inside {
8994 *close.start()
8995 } else {
8996 *close.end()
8997 },
8998 );
8999 }
9000
9001 if let Some(destination) = best_destination {
9002 selection.collapse_to(destination, SelectionGoal::None);
9003 }
9004 })
9005 });
9006 }
9007
9008 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
9009 self.end_selection(cx);
9010 self.selection_history.mode = SelectionHistoryMode::Undoing;
9011 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9012 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9013 self.select_next_state = entry.select_next_state;
9014 self.select_prev_state = entry.select_prev_state;
9015 self.add_selections_state = entry.add_selections_state;
9016 self.request_autoscroll(Autoscroll::newest(), cx);
9017 }
9018 self.selection_history.mode = SelectionHistoryMode::Normal;
9019 }
9020
9021 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
9022 self.end_selection(cx);
9023 self.selection_history.mode = SelectionHistoryMode::Redoing;
9024 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9025 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9026 self.select_next_state = entry.select_next_state;
9027 self.select_prev_state = entry.select_prev_state;
9028 self.add_selections_state = entry.add_selections_state;
9029 self.request_autoscroll(Autoscroll::newest(), cx);
9030 }
9031 self.selection_history.mode = SelectionHistoryMode::Normal;
9032 }
9033
9034 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
9035 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9036 }
9037
9038 pub fn expand_excerpts_down(
9039 &mut self,
9040 action: &ExpandExcerptsDown,
9041 cx: &mut ViewContext<Self>,
9042 ) {
9043 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9044 }
9045
9046 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
9047 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9048 }
9049
9050 pub fn expand_excerpts_for_direction(
9051 &mut self,
9052 lines: u32,
9053 direction: ExpandExcerptDirection,
9054 cx: &mut ViewContext<Self>,
9055 ) {
9056 let selections = self.selections.disjoint_anchors();
9057
9058 let lines = if lines == 0 {
9059 EditorSettings::get_global(cx).expand_excerpt_lines
9060 } else {
9061 lines
9062 };
9063
9064 self.buffer.update(cx, |buffer, cx| {
9065 buffer.expand_excerpts(
9066 selections
9067 .iter()
9068 .map(|selection| selection.head().excerpt_id)
9069 .dedup(),
9070 lines,
9071 direction,
9072 cx,
9073 )
9074 })
9075 }
9076
9077 pub fn expand_excerpt(
9078 &mut self,
9079 excerpt: ExcerptId,
9080 direction: ExpandExcerptDirection,
9081 cx: &mut ViewContext<Self>,
9082 ) {
9083 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9084 self.buffer.update(cx, |buffer, cx| {
9085 buffer.expand_excerpts([excerpt], lines, direction, cx)
9086 })
9087 }
9088
9089 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
9090 self.go_to_diagnostic_impl(Direction::Next, cx)
9091 }
9092
9093 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9094 self.go_to_diagnostic_impl(Direction::Prev, cx)
9095 }
9096
9097 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9098 let buffer = self.buffer.read(cx).snapshot(cx);
9099 let selection = self.selections.newest::<usize>(cx);
9100
9101 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9102 if direction == Direction::Next {
9103 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9104 let (group_id, jump_to) = popover.activation_info();
9105 if self.activate_diagnostics(group_id, cx) {
9106 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9107 let mut new_selection = s.newest_anchor().clone();
9108 new_selection.collapse_to(jump_to, SelectionGoal::None);
9109 s.select_anchors(vec![new_selection.clone()]);
9110 });
9111 }
9112 return;
9113 }
9114 }
9115
9116 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9117 active_diagnostics
9118 .primary_range
9119 .to_offset(&buffer)
9120 .to_inclusive()
9121 });
9122 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9123 if active_primary_range.contains(&selection.head()) {
9124 *active_primary_range.start()
9125 } else {
9126 selection.head()
9127 }
9128 } else {
9129 selection.head()
9130 };
9131 let snapshot = self.snapshot(cx);
9132 loop {
9133 let diagnostics = if direction == Direction::Prev {
9134 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
9135 } else {
9136 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
9137 }
9138 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9139 let group = diagnostics
9140 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9141 // be sorted in a stable way
9142 // skip until we are at current active diagnostic, if it exists
9143 .skip_while(|entry| {
9144 (match direction {
9145 Direction::Prev => entry.range.start >= search_start,
9146 Direction::Next => entry.range.start <= search_start,
9147 }) && self
9148 .active_diagnostics
9149 .as_ref()
9150 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9151 })
9152 .find_map(|entry| {
9153 if entry.diagnostic.is_primary
9154 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9155 && !entry.range.is_empty()
9156 // if we match with the active diagnostic, skip it
9157 && Some(entry.diagnostic.group_id)
9158 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9159 {
9160 Some((entry.range, entry.diagnostic.group_id))
9161 } else {
9162 None
9163 }
9164 });
9165
9166 if let Some((primary_range, group_id)) = group {
9167 if self.activate_diagnostics(group_id, cx) {
9168 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9169 s.select(vec![Selection {
9170 id: selection.id,
9171 start: primary_range.start,
9172 end: primary_range.start,
9173 reversed: false,
9174 goal: SelectionGoal::None,
9175 }]);
9176 });
9177 }
9178 break;
9179 } else {
9180 // Cycle around to the start of the buffer, potentially moving back to the start of
9181 // the currently active diagnostic.
9182 active_primary_range.take();
9183 if direction == Direction::Prev {
9184 if search_start == buffer.len() {
9185 break;
9186 } else {
9187 search_start = buffer.len();
9188 }
9189 } else if search_start == 0 {
9190 break;
9191 } else {
9192 search_start = 0;
9193 }
9194 }
9195 }
9196 }
9197
9198 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9199 let snapshot = self
9200 .display_map
9201 .update(cx, |display_map, cx| display_map.snapshot(cx));
9202 let selection = self.selections.newest::<Point>(cx);
9203
9204 if !self.seek_in_direction(
9205 &snapshot,
9206 selection.head(),
9207 false,
9208 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9209 MultiBufferRow(selection.head().row + 1)..MultiBufferRow::MAX,
9210 ),
9211 cx,
9212 ) {
9213 let wrapped_point = Point::zero();
9214 self.seek_in_direction(
9215 &snapshot,
9216 wrapped_point,
9217 true,
9218 snapshot.buffer_snapshot.git_diff_hunks_in_range(
9219 MultiBufferRow(wrapped_point.row + 1)..MultiBufferRow::MAX,
9220 ),
9221 cx,
9222 );
9223 }
9224 }
9225
9226 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9227 let snapshot = self
9228 .display_map
9229 .update(cx, |display_map, cx| display_map.snapshot(cx));
9230 let selection = self.selections.newest::<Point>(cx);
9231
9232 if !self.seek_in_direction(
9233 &snapshot,
9234 selection.head(),
9235 false,
9236 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
9237 MultiBufferRow(0)..MultiBufferRow(selection.head().row),
9238 ),
9239 cx,
9240 ) {
9241 let wrapped_point = snapshot.buffer_snapshot.max_point();
9242 self.seek_in_direction(
9243 &snapshot,
9244 wrapped_point,
9245 true,
9246 snapshot.buffer_snapshot.git_diff_hunks_in_range_rev(
9247 MultiBufferRow(0)..MultiBufferRow(wrapped_point.row),
9248 ),
9249 cx,
9250 );
9251 }
9252 }
9253
9254 fn seek_in_direction(
9255 &mut self,
9256 snapshot: &DisplaySnapshot,
9257 initial_point: Point,
9258 is_wrapped: bool,
9259 hunks: impl Iterator<Item = DiffHunk<MultiBufferRow>>,
9260 cx: &mut ViewContext<Editor>,
9261 ) -> bool {
9262 let display_point = initial_point.to_display_point(snapshot);
9263 let mut hunks = hunks
9264 .map(|hunk| diff_hunk_to_display(&hunk, snapshot))
9265 .filter(|hunk| is_wrapped || !hunk.contains_display_row(display_point.row()))
9266 .dedup();
9267
9268 if let Some(hunk) = hunks.next() {
9269 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9270 let row = hunk.start_display_row();
9271 let point = DisplayPoint::new(row, 0);
9272 s.select_display_ranges([point..point]);
9273 });
9274
9275 true
9276 } else {
9277 false
9278 }
9279 }
9280
9281 pub fn go_to_definition(
9282 &mut self,
9283 _: &GoToDefinition,
9284 cx: &mut ViewContext<Self>,
9285 ) -> Task<Result<Navigated>> {
9286 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9287 cx.spawn(|editor, mut cx| async move {
9288 if definition.await? == Navigated::Yes {
9289 return Ok(Navigated::Yes);
9290 }
9291 match editor.update(&mut cx, |editor, cx| {
9292 editor.find_all_references(&FindAllReferences, cx)
9293 })? {
9294 Some(references) => references.await,
9295 None => Ok(Navigated::No),
9296 }
9297 })
9298 }
9299
9300 pub fn go_to_declaration(
9301 &mut self,
9302 _: &GoToDeclaration,
9303 cx: &mut ViewContext<Self>,
9304 ) -> Task<Result<Navigated>> {
9305 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9306 }
9307
9308 pub fn go_to_declaration_split(
9309 &mut self,
9310 _: &GoToDeclaration,
9311 cx: &mut ViewContext<Self>,
9312 ) -> Task<Result<Navigated>> {
9313 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9314 }
9315
9316 pub fn go_to_implementation(
9317 &mut self,
9318 _: &GoToImplementation,
9319 cx: &mut ViewContext<Self>,
9320 ) -> Task<Result<Navigated>> {
9321 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9322 }
9323
9324 pub fn go_to_implementation_split(
9325 &mut self,
9326 _: &GoToImplementationSplit,
9327 cx: &mut ViewContext<Self>,
9328 ) -> Task<Result<Navigated>> {
9329 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9330 }
9331
9332 pub fn go_to_type_definition(
9333 &mut self,
9334 _: &GoToTypeDefinition,
9335 cx: &mut ViewContext<Self>,
9336 ) -> Task<Result<Navigated>> {
9337 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9338 }
9339
9340 pub fn go_to_definition_split(
9341 &mut self,
9342 _: &GoToDefinitionSplit,
9343 cx: &mut ViewContext<Self>,
9344 ) -> Task<Result<Navigated>> {
9345 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9346 }
9347
9348 pub fn go_to_type_definition_split(
9349 &mut self,
9350 _: &GoToTypeDefinitionSplit,
9351 cx: &mut ViewContext<Self>,
9352 ) -> Task<Result<Navigated>> {
9353 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9354 }
9355
9356 fn go_to_definition_of_kind(
9357 &mut self,
9358 kind: GotoDefinitionKind,
9359 split: bool,
9360 cx: &mut ViewContext<Self>,
9361 ) -> Task<Result<Navigated>> {
9362 let Some(workspace) = self.workspace() else {
9363 return Task::ready(Ok(Navigated::No));
9364 };
9365 let buffer = self.buffer.read(cx);
9366 let head = self.selections.newest::<usize>(cx).head();
9367 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9368 text_anchor
9369 } else {
9370 return Task::ready(Ok(Navigated::No));
9371 };
9372
9373 let project = workspace.read(cx).project().clone();
9374 let definitions = project.update(cx, |project, cx| match kind {
9375 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
9376 GotoDefinitionKind::Declaration => project.declaration(&buffer, head, cx),
9377 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
9378 GotoDefinitionKind::Implementation => project.implementation(&buffer, head, cx),
9379 });
9380
9381 cx.spawn(|editor, mut cx| async move {
9382 let definitions = definitions.await?;
9383 let navigated = editor
9384 .update(&mut cx, |editor, cx| {
9385 editor.navigate_to_hover_links(
9386 Some(kind),
9387 definitions
9388 .into_iter()
9389 .filter(|location| {
9390 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9391 })
9392 .map(HoverLink::Text)
9393 .collect::<Vec<_>>(),
9394 split,
9395 cx,
9396 )
9397 })?
9398 .await?;
9399 anyhow::Ok(navigated)
9400 })
9401 }
9402
9403 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9404 let position = self.selections.newest_anchor().head();
9405 let Some((buffer, buffer_position)) =
9406 self.buffer.read(cx).text_anchor_for_position(position, cx)
9407 else {
9408 return;
9409 };
9410
9411 cx.spawn(|editor, mut cx| async move {
9412 if let Some((_, url)) = find_url(&buffer, buffer_position, cx.clone()) {
9413 editor.update(&mut cx, |_, cx| {
9414 cx.open_url(&url);
9415 })
9416 } else {
9417 Ok(())
9418 }
9419 })
9420 .detach();
9421 }
9422
9423 pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext<Self>) {
9424 let Some(workspace) = self.workspace() else {
9425 return;
9426 };
9427
9428 let position = self.selections.newest_anchor().head();
9429
9430 let Some((buffer, buffer_position)) =
9431 self.buffer.read(cx).text_anchor_for_position(position, cx)
9432 else {
9433 return;
9434 };
9435
9436 let Some(project) = self.project.clone() else {
9437 return;
9438 };
9439
9440 cx.spawn(|_, mut cx| async move {
9441 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9442
9443 if let Some((_, path)) = result {
9444 workspace
9445 .update(&mut cx, |workspace, cx| {
9446 workspace.open_resolved_path(path, cx)
9447 })?
9448 .await?;
9449 }
9450 anyhow::Ok(())
9451 })
9452 .detach();
9453 }
9454
9455 pub(crate) fn navigate_to_hover_links(
9456 &mut self,
9457 kind: Option<GotoDefinitionKind>,
9458 mut definitions: Vec<HoverLink>,
9459 split: bool,
9460 cx: &mut ViewContext<Editor>,
9461 ) -> Task<Result<Navigated>> {
9462 // If there is one definition, just open it directly
9463 if definitions.len() == 1 {
9464 let definition = definitions.pop().unwrap();
9465
9466 enum TargetTaskResult {
9467 Location(Option<Location>),
9468 AlreadyNavigated,
9469 }
9470
9471 let target_task = match definition {
9472 HoverLink::Text(link) => {
9473 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9474 }
9475 HoverLink::InlayHint(lsp_location, server_id) => {
9476 let computation = self.compute_target_location(lsp_location, server_id, cx);
9477 cx.background_executor().spawn(async move {
9478 let location = computation.await?;
9479 Ok(TargetTaskResult::Location(location))
9480 })
9481 }
9482 HoverLink::Url(url) => {
9483 cx.open_url(&url);
9484 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9485 }
9486 HoverLink::File(path) => {
9487 if let Some(workspace) = self.workspace() {
9488 cx.spawn(|_, mut cx| async move {
9489 workspace
9490 .update(&mut cx, |workspace, cx| {
9491 workspace.open_resolved_path(path, cx)
9492 })?
9493 .await
9494 .map(|_| TargetTaskResult::AlreadyNavigated)
9495 })
9496 } else {
9497 Task::ready(Ok(TargetTaskResult::Location(None)))
9498 }
9499 }
9500 };
9501 cx.spawn(|editor, mut cx| async move {
9502 let target = match target_task.await.context("target resolution task")? {
9503 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9504 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9505 TargetTaskResult::Location(Some(target)) => target,
9506 };
9507
9508 editor.update(&mut cx, |editor, cx| {
9509 let Some(workspace) = editor.workspace() else {
9510 return Navigated::No;
9511 };
9512 let pane = workspace.read(cx).active_pane().clone();
9513
9514 let range = target.range.to_offset(target.buffer.read(cx));
9515 let range = editor.range_for_match(&range);
9516
9517 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9518 let buffer = target.buffer.read(cx);
9519 let range = check_multiline_range(buffer, range);
9520 editor.change_selections(Some(Autoscroll::focused()), cx, |s| {
9521 s.select_ranges([range]);
9522 });
9523 } else {
9524 cx.window_context().defer(move |cx| {
9525 let target_editor: View<Self> =
9526 workspace.update(cx, |workspace, cx| {
9527 let pane = if split {
9528 workspace.adjacent_pane(cx)
9529 } else {
9530 workspace.active_pane().clone()
9531 };
9532
9533 workspace.open_project_item(
9534 pane,
9535 target.buffer.clone(),
9536 true,
9537 true,
9538 cx,
9539 )
9540 });
9541 target_editor.update(cx, |target_editor, cx| {
9542 // When selecting a definition in a different buffer, disable the nav history
9543 // to avoid creating a history entry at the previous cursor location.
9544 pane.update(cx, |pane, _| pane.disable_history());
9545 let buffer = target.buffer.read(cx);
9546 let range = check_multiline_range(buffer, range);
9547 target_editor.change_selections(
9548 Some(Autoscroll::focused()),
9549 cx,
9550 |s| {
9551 s.select_ranges([range]);
9552 },
9553 );
9554 pane.update(cx, |pane, _| pane.enable_history());
9555 });
9556 });
9557 }
9558 Navigated::Yes
9559 })
9560 })
9561 } else if !definitions.is_empty() {
9562 let replica_id = self.replica_id(cx);
9563 cx.spawn(|editor, mut cx| async move {
9564 let (title, location_tasks, workspace) = editor
9565 .update(&mut cx, |editor, cx| {
9566 let tab_kind = match kind {
9567 Some(GotoDefinitionKind::Implementation) => "Implementations",
9568 _ => "Definitions",
9569 };
9570 let title = definitions
9571 .iter()
9572 .find_map(|definition| match definition {
9573 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9574 let buffer = origin.buffer.read(cx);
9575 format!(
9576 "{} for {}",
9577 tab_kind,
9578 buffer
9579 .text_for_range(origin.range.clone())
9580 .collect::<String>()
9581 )
9582 }),
9583 HoverLink::InlayHint(_, _) => None,
9584 HoverLink::Url(_) => None,
9585 HoverLink::File(_) => None,
9586 })
9587 .unwrap_or(tab_kind.to_string());
9588 let location_tasks = definitions
9589 .into_iter()
9590 .map(|definition| match definition {
9591 HoverLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
9592 HoverLink::InlayHint(lsp_location, server_id) => {
9593 editor.compute_target_location(lsp_location, server_id, cx)
9594 }
9595 HoverLink::Url(_) => Task::ready(Ok(None)),
9596 HoverLink::File(_) => Task::ready(Ok(None)),
9597 })
9598 .collect::<Vec<_>>();
9599 (title, location_tasks, editor.workspace().clone())
9600 })
9601 .context("location tasks preparation")?;
9602
9603 let locations = futures::future::join_all(location_tasks)
9604 .await
9605 .into_iter()
9606 .filter_map(|location| location.transpose())
9607 .collect::<Result<_>>()
9608 .context("location tasks")?;
9609
9610 let Some(workspace) = workspace else {
9611 return Ok(Navigated::No);
9612 };
9613 let opened = workspace
9614 .update(&mut cx, |workspace, cx| {
9615 Self::open_locations_in_multibuffer(
9616 workspace, locations, replica_id, title, split, cx,
9617 )
9618 })
9619 .ok();
9620
9621 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9622 })
9623 } else {
9624 Task::ready(Ok(Navigated::No))
9625 }
9626 }
9627
9628 fn compute_target_location(
9629 &self,
9630 lsp_location: lsp::Location,
9631 server_id: LanguageServerId,
9632 cx: &mut ViewContext<Editor>,
9633 ) -> Task<anyhow::Result<Option<Location>>> {
9634 let Some(project) = self.project.clone() else {
9635 return Task::Ready(Some(Ok(None)));
9636 };
9637
9638 cx.spawn(move |editor, mut cx| async move {
9639 let location_task = editor.update(&mut cx, |editor, cx| {
9640 project.update(cx, |project, cx| {
9641 let language_server_name =
9642 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
9643 project
9644 .language_server_for_buffer(buffer.read(cx), server_id, cx)
9645 .map(|(lsp_adapter, _)| lsp_adapter.name.clone())
9646 });
9647 language_server_name.map(|language_server_name| {
9648 project.open_local_buffer_via_lsp(
9649 lsp_location.uri.clone(),
9650 server_id,
9651 language_server_name,
9652 cx,
9653 )
9654 })
9655 })
9656 })?;
9657 let location = match location_task {
9658 Some(task) => Some({
9659 let target_buffer_handle = task.await.context("open local buffer")?;
9660 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9661 let target_start = target_buffer
9662 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9663 let target_end = target_buffer
9664 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9665 target_buffer.anchor_after(target_start)
9666 ..target_buffer.anchor_before(target_end)
9667 })?;
9668 Location {
9669 buffer: target_buffer_handle,
9670 range,
9671 }
9672 }),
9673 None => None,
9674 };
9675 Ok(location)
9676 })
9677 }
9678
9679 pub fn find_all_references(
9680 &mut self,
9681 _: &FindAllReferences,
9682 cx: &mut ViewContext<Self>,
9683 ) -> Option<Task<Result<Navigated>>> {
9684 let multi_buffer = self.buffer.read(cx);
9685 let selection = self.selections.newest::<usize>(cx);
9686 let head = selection.head();
9687
9688 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9689 let head_anchor = multi_buffer_snapshot.anchor_at(
9690 head,
9691 if head < selection.tail() {
9692 Bias::Right
9693 } else {
9694 Bias::Left
9695 },
9696 );
9697
9698 match self
9699 .find_all_references_task_sources
9700 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9701 {
9702 Ok(_) => {
9703 log::info!(
9704 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9705 );
9706 return None;
9707 }
9708 Err(i) => {
9709 self.find_all_references_task_sources.insert(i, head_anchor);
9710 }
9711 }
9712
9713 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9714 let replica_id = self.replica_id(cx);
9715 let workspace = self.workspace()?;
9716 let project = workspace.read(cx).project().clone();
9717 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9718 Some(cx.spawn(|editor, mut cx| async move {
9719 let _cleanup = defer({
9720 let mut cx = cx.clone();
9721 move || {
9722 let _ = editor.update(&mut cx, |editor, _| {
9723 if let Ok(i) =
9724 editor
9725 .find_all_references_task_sources
9726 .binary_search_by(|anchor| {
9727 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9728 })
9729 {
9730 editor.find_all_references_task_sources.remove(i);
9731 }
9732 });
9733 }
9734 });
9735
9736 let locations = references.await?;
9737 if locations.is_empty() {
9738 return anyhow::Ok(Navigated::No);
9739 }
9740
9741 workspace.update(&mut cx, |workspace, cx| {
9742 let title = locations
9743 .first()
9744 .as_ref()
9745 .map(|location| {
9746 let buffer = location.buffer.read(cx);
9747 format!(
9748 "References to `{}`",
9749 buffer
9750 .text_for_range(location.range.clone())
9751 .collect::<String>()
9752 )
9753 })
9754 .unwrap();
9755 Self::open_locations_in_multibuffer(
9756 workspace, locations, replica_id, title, false, cx,
9757 );
9758 Navigated::Yes
9759 })
9760 }))
9761 }
9762
9763 /// Opens a multibuffer with the given project locations in it
9764 pub fn open_locations_in_multibuffer(
9765 workspace: &mut Workspace,
9766 mut locations: Vec<Location>,
9767 replica_id: ReplicaId,
9768 title: String,
9769 split: bool,
9770 cx: &mut ViewContext<Workspace>,
9771 ) {
9772 // If there are multiple definitions, open them in a multibuffer
9773 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9774 let mut locations = locations.into_iter().peekable();
9775 let mut ranges_to_highlight = Vec::new();
9776 let capability = workspace.project().read(cx).capability();
9777
9778 let excerpt_buffer = cx.new_model(|cx| {
9779 let mut multibuffer = MultiBuffer::new(replica_id, capability);
9780 while let Some(location) = locations.next() {
9781 let buffer = location.buffer.read(cx);
9782 let mut ranges_for_buffer = Vec::new();
9783 let range = location.range.to_offset(buffer);
9784 ranges_for_buffer.push(range.clone());
9785
9786 while let Some(next_location) = locations.peek() {
9787 if next_location.buffer == location.buffer {
9788 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9789 locations.next();
9790 } else {
9791 break;
9792 }
9793 }
9794
9795 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9796 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9797 location.buffer.clone(),
9798 ranges_for_buffer,
9799 DEFAULT_MULTIBUFFER_CONTEXT,
9800 cx,
9801 ))
9802 }
9803
9804 multibuffer.with_title(title)
9805 });
9806
9807 let editor = cx.new_view(|cx| {
9808 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9809 });
9810 editor.update(cx, |editor, cx| {
9811 if let Some(first_range) = ranges_to_highlight.first() {
9812 editor.change_selections(None, cx, |selections| {
9813 selections.clear_disjoint();
9814 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9815 });
9816 }
9817 editor.highlight_background::<Self>(
9818 &ranges_to_highlight,
9819 |theme| theme.editor_highlighted_line_background,
9820 cx,
9821 );
9822 });
9823
9824 let item = Box::new(editor);
9825 let item_id = item.item_id();
9826
9827 if split {
9828 workspace.split_item(SplitDirection::Right, item.clone(), cx);
9829 } else {
9830 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
9831 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
9832 pane.close_current_preview_item(cx)
9833 } else {
9834 None
9835 }
9836 });
9837 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
9838 }
9839 workspace.active_pane().update(cx, |pane, cx| {
9840 pane.set_preview_item_id(Some(item_id), cx);
9841 });
9842 }
9843
9844 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
9845 use language::ToOffset as _;
9846
9847 let project = self.project.clone()?;
9848 let selection = self.selections.newest_anchor().clone();
9849 let (cursor_buffer, cursor_buffer_position) = self
9850 .buffer
9851 .read(cx)
9852 .text_anchor_for_position(selection.head(), cx)?;
9853 let (tail_buffer, cursor_buffer_position_end) = self
9854 .buffer
9855 .read(cx)
9856 .text_anchor_for_position(selection.tail(), cx)?;
9857 if tail_buffer != cursor_buffer {
9858 return None;
9859 }
9860
9861 let snapshot = cursor_buffer.read(cx).snapshot();
9862 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
9863 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
9864 let prepare_rename = project.update(cx, |project, cx| {
9865 project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
9866 });
9867 drop(snapshot);
9868
9869 Some(cx.spawn(|this, mut cx| async move {
9870 let rename_range = if let Some(range) = prepare_rename.await? {
9871 Some(range)
9872 } else {
9873 this.update(&mut cx, |this, cx| {
9874 let buffer = this.buffer.read(cx).snapshot(cx);
9875 let mut buffer_highlights = this
9876 .document_highlights_for_position(selection.head(), &buffer)
9877 .filter(|highlight| {
9878 highlight.start.excerpt_id == selection.head().excerpt_id
9879 && highlight.end.excerpt_id == selection.head().excerpt_id
9880 });
9881 buffer_highlights
9882 .next()
9883 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
9884 })?
9885 };
9886 if let Some(rename_range) = rename_range {
9887 this.update(&mut cx, |this, cx| {
9888 let snapshot = cursor_buffer.read(cx).snapshot();
9889 let rename_buffer_range = rename_range.to_offset(&snapshot);
9890 let cursor_offset_in_rename_range =
9891 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
9892 let cursor_offset_in_rename_range_end =
9893 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
9894
9895 this.take_rename(false, cx);
9896 let buffer = this.buffer.read(cx).read(cx);
9897 let cursor_offset = selection.head().to_offset(&buffer);
9898 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
9899 let rename_end = rename_start + rename_buffer_range.len();
9900 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
9901 let mut old_highlight_id = None;
9902 let old_name: Arc<str> = buffer
9903 .chunks(rename_start..rename_end, true)
9904 .map(|chunk| {
9905 if old_highlight_id.is_none() {
9906 old_highlight_id = chunk.syntax_highlight_id;
9907 }
9908 chunk.text
9909 })
9910 .collect::<String>()
9911 .into();
9912
9913 drop(buffer);
9914
9915 // Position the selection in the rename editor so that it matches the current selection.
9916 this.show_local_selections = false;
9917 let rename_editor = cx.new_view(|cx| {
9918 let mut editor = Editor::single_line(cx);
9919 editor.buffer.update(cx, |buffer, cx| {
9920 buffer.edit([(0..0, old_name.clone())], None, cx)
9921 });
9922 let rename_selection_range = match cursor_offset_in_rename_range
9923 .cmp(&cursor_offset_in_rename_range_end)
9924 {
9925 Ordering::Equal => {
9926 editor.select_all(&SelectAll, cx);
9927 return editor;
9928 }
9929 Ordering::Less => {
9930 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
9931 }
9932 Ordering::Greater => {
9933 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
9934 }
9935 };
9936 if rename_selection_range.end > old_name.len() {
9937 editor.select_all(&SelectAll, cx);
9938 } else {
9939 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9940 s.select_ranges([rename_selection_range]);
9941 });
9942 }
9943 editor
9944 });
9945 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
9946 if e == &EditorEvent::Focused {
9947 cx.emit(EditorEvent::FocusedIn)
9948 }
9949 })
9950 .detach();
9951
9952 let write_highlights =
9953 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
9954 let read_highlights =
9955 this.clear_background_highlights::<DocumentHighlightRead>(cx);
9956 let ranges = write_highlights
9957 .iter()
9958 .flat_map(|(_, ranges)| ranges.iter())
9959 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
9960 .cloned()
9961 .collect();
9962
9963 this.highlight_text::<Rename>(
9964 ranges,
9965 HighlightStyle {
9966 fade_out: Some(0.6),
9967 ..Default::default()
9968 },
9969 cx,
9970 );
9971 let rename_focus_handle = rename_editor.focus_handle(cx);
9972 cx.focus(&rename_focus_handle);
9973 let block_id = this.insert_blocks(
9974 [BlockProperties {
9975 style: BlockStyle::Flex,
9976 position: range.start,
9977 height: 1,
9978 render: Box::new({
9979 let rename_editor = rename_editor.clone();
9980 move |cx: &mut BlockContext| {
9981 let mut text_style = cx.editor_style.text.clone();
9982 if let Some(highlight_style) = old_highlight_id
9983 .and_then(|h| h.style(&cx.editor_style.syntax))
9984 {
9985 text_style = text_style.highlight(highlight_style);
9986 }
9987 div()
9988 .pl(cx.anchor_x)
9989 .child(EditorElement::new(
9990 &rename_editor,
9991 EditorStyle {
9992 background: cx.theme().system().transparent,
9993 local_player: cx.editor_style.local_player,
9994 text: text_style,
9995 scrollbar_width: cx.editor_style.scrollbar_width,
9996 syntax: cx.editor_style.syntax.clone(),
9997 status: cx.editor_style.status.clone(),
9998 inlay_hints_style: HighlightStyle {
9999 color: Some(cx.theme().status().hint),
10000 font_weight: Some(FontWeight::BOLD),
10001 ..HighlightStyle::default()
10002 },
10003 suggestions_style: HighlightStyle {
10004 color: Some(cx.theme().status().predictive),
10005 ..HighlightStyle::default()
10006 },
10007 ..EditorStyle::default()
10008 },
10009 ))
10010 .into_any_element()
10011 }
10012 }),
10013 disposition: BlockDisposition::Below,
10014 priority: 0,
10015 }],
10016 Some(Autoscroll::fit()),
10017 cx,
10018 )[0];
10019 this.pending_rename = Some(RenameState {
10020 range,
10021 old_name,
10022 editor: rename_editor,
10023 block_id,
10024 });
10025 })?;
10026 }
10027
10028 Ok(())
10029 }))
10030 }
10031
10032 pub fn confirm_rename(
10033 &mut self,
10034 _: &ConfirmRename,
10035 cx: &mut ViewContext<Self>,
10036 ) -> Option<Task<Result<()>>> {
10037 let rename = self.take_rename(false, cx)?;
10038 let workspace = self.workspace()?;
10039 let (start_buffer, start) = self
10040 .buffer
10041 .read(cx)
10042 .text_anchor_for_position(rename.range.start, cx)?;
10043 let (end_buffer, end) = self
10044 .buffer
10045 .read(cx)
10046 .text_anchor_for_position(rename.range.end, cx)?;
10047 if start_buffer != end_buffer {
10048 return None;
10049 }
10050
10051 let buffer = start_buffer;
10052 let range = start..end;
10053 let old_name = rename.old_name;
10054 let new_name = rename.editor.read(cx).text(cx);
10055
10056 let rename = workspace
10057 .read(cx)
10058 .project()
10059 .clone()
10060 .update(cx, |project, cx| {
10061 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
10062 });
10063 let workspace = workspace.downgrade();
10064
10065 Some(cx.spawn(|editor, mut cx| async move {
10066 let project_transaction = rename.await?;
10067 Self::open_project_transaction(
10068 &editor,
10069 workspace,
10070 project_transaction,
10071 format!("Rename: {} → {}", old_name, new_name),
10072 cx.clone(),
10073 )
10074 .await?;
10075
10076 editor.update(&mut cx, |editor, cx| {
10077 editor.refresh_document_highlights(cx);
10078 })?;
10079 Ok(())
10080 }))
10081 }
10082
10083 fn take_rename(
10084 &mut self,
10085 moving_cursor: bool,
10086 cx: &mut ViewContext<Self>,
10087 ) -> Option<RenameState> {
10088 let rename = self.pending_rename.take()?;
10089 if rename.editor.focus_handle(cx).is_focused(cx) {
10090 cx.focus(&self.focus_handle);
10091 }
10092
10093 self.remove_blocks(
10094 [rename.block_id].into_iter().collect(),
10095 Some(Autoscroll::fit()),
10096 cx,
10097 );
10098 self.clear_highlights::<Rename>(cx);
10099 self.show_local_selections = true;
10100
10101 if moving_cursor {
10102 let rename_editor = rename.editor.read(cx);
10103 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
10104
10105 // Update the selection to match the position of the selection inside
10106 // the rename editor.
10107 let snapshot = self.buffer.read(cx).read(cx);
10108 let rename_range = rename.range.to_offset(&snapshot);
10109 let cursor_in_editor = snapshot
10110 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10111 .min(rename_range.end);
10112 drop(snapshot);
10113
10114 self.change_selections(None, cx, |s| {
10115 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10116 });
10117 } else {
10118 self.refresh_document_highlights(cx);
10119 }
10120
10121 Some(rename)
10122 }
10123
10124 pub fn pending_rename(&self) -> Option<&RenameState> {
10125 self.pending_rename.as_ref()
10126 }
10127
10128 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10129 let project = match &self.project {
10130 Some(project) => project.clone(),
10131 None => return None,
10132 };
10133
10134 Some(self.perform_format(project, FormatTrigger::Manual, cx))
10135 }
10136
10137 fn perform_format(
10138 &mut self,
10139 project: Model<Project>,
10140 trigger: FormatTrigger,
10141 cx: &mut ViewContext<Self>,
10142 ) -> Task<Result<()>> {
10143 let buffer = self.buffer().clone();
10144 let mut buffers = buffer.read(cx).all_buffers();
10145 if trigger == FormatTrigger::Save {
10146 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10147 }
10148
10149 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10150 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
10151
10152 cx.spawn(|_, mut cx| async move {
10153 let transaction = futures::select_biased! {
10154 () = timeout => {
10155 log::warn!("timed out waiting for formatting");
10156 None
10157 }
10158 transaction = format.log_err().fuse() => transaction,
10159 };
10160
10161 buffer
10162 .update(&mut cx, |buffer, cx| {
10163 if let Some(transaction) = transaction {
10164 if !buffer.is_singleton() {
10165 buffer.push_transaction(&transaction.0, cx);
10166 }
10167 }
10168
10169 cx.notify();
10170 })
10171 .ok();
10172
10173 Ok(())
10174 })
10175 }
10176
10177 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10178 if let Some(project) = self.project.clone() {
10179 self.buffer.update(cx, |multi_buffer, cx| {
10180 project.update(cx, |project, cx| {
10181 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10182 });
10183 })
10184 }
10185 }
10186
10187 fn cancel_language_server_work(
10188 &mut self,
10189 _: &CancelLanguageServerWork,
10190 cx: &mut ViewContext<Self>,
10191 ) {
10192 if let Some(project) = self.project.clone() {
10193 self.buffer.update(cx, |multi_buffer, cx| {
10194 project.update(cx, |project, cx| {
10195 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10196 });
10197 })
10198 }
10199 }
10200
10201 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10202 cx.show_character_palette();
10203 }
10204
10205 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10206 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10207 let buffer = self.buffer.read(cx).snapshot(cx);
10208 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10209 let is_valid = buffer
10210 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
10211 .any(|entry| {
10212 entry.diagnostic.is_primary
10213 && !entry.range.is_empty()
10214 && entry.range.start == primary_range_start
10215 && entry.diagnostic.message == active_diagnostics.primary_message
10216 });
10217
10218 if is_valid != active_diagnostics.is_valid {
10219 active_diagnostics.is_valid = is_valid;
10220 let mut new_styles = HashMap::default();
10221 for (block_id, diagnostic) in &active_diagnostics.blocks {
10222 new_styles.insert(
10223 *block_id,
10224 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10225 );
10226 }
10227 self.display_map.update(cx, |display_map, _cx| {
10228 display_map.replace_blocks(new_styles)
10229 });
10230 }
10231 }
10232 }
10233
10234 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
10235 self.dismiss_diagnostics(cx);
10236 let snapshot = self.snapshot(cx);
10237 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10238 let buffer = self.buffer.read(cx).snapshot(cx);
10239
10240 let mut primary_range = None;
10241 let mut primary_message = None;
10242 let mut group_end = Point::zero();
10243 let diagnostic_group = buffer
10244 .diagnostic_group::<MultiBufferPoint>(group_id)
10245 .filter_map(|entry| {
10246 if snapshot.is_line_folded(MultiBufferRow(entry.range.start.row))
10247 && (entry.range.start.row == entry.range.end.row
10248 || snapshot.is_line_folded(MultiBufferRow(entry.range.end.row)))
10249 {
10250 return None;
10251 }
10252 if entry.range.end > group_end {
10253 group_end = entry.range.end;
10254 }
10255 if entry.diagnostic.is_primary {
10256 primary_range = Some(entry.range.clone());
10257 primary_message = Some(entry.diagnostic.message.clone());
10258 }
10259 Some(entry)
10260 })
10261 .collect::<Vec<_>>();
10262 let primary_range = primary_range?;
10263 let primary_message = primary_message?;
10264 let primary_range =
10265 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
10266
10267 let blocks = display_map
10268 .insert_blocks(
10269 diagnostic_group.iter().map(|entry| {
10270 let diagnostic = entry.diagnostic.clone();
10271 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10272 BlockProperties {
10273 style: BlockStyle::Fixed,
10274 position: buffer.anchor_after(entry.range.start),
10275 height: message_height,
10276 render: diagnostic_block_renderer(diagnostic, None, true, true),
10277 disposition: BlockDisposition::Below,
10278 priority: 0,
10279 }
10280 }),
10281 cx,
10282 )
10283 .into_iter()
10284 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10285 .collect();
10286
10287 Some(ActiveDiagnosticGroup {
10288 primary_range,
10289 primary_message,
10290 group_id,
10291 blocks,
10292 is_valid: true,
10293 })
10294 });
10295 self.active_diagnostics.is_some()
10296 }
10297
10298 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10299 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10300 self.display_map.update(cx, |display_map, cx| {
10301 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10302 });
10303 cx.notify();
10304 }
10305 }
10306
10307 pub fn set_selections_from_remote(
10308 &mut self,
10309 selections: Vec<Selection<Anchor>>,
10310 pending_selection: Option<Selection<Anchor>>,
10311 cx: &mut ViewContext<Self>,
10312 ) {
10313 let old_cursor_position = self.selections.newest_anchor().head();
10314 self.selections.change_with(cx, |s| {
10315 s.select_anchors(selections);
10316 if let Some(pending_selection) = pending_selection {
10317 s.set_pending(pending_selection, SelectMode::Character);
10318 } else {
10319 s.clear_pending();
10320 }
10321 });
10322 self.selections_did_change(false, &old_cursor_position, true, cx);
10323 }
10324
10325 fn push_to_selection_history(&mut self) {
10326 self.selection_history.push(SelectionHistoryEntry {
10327 selections: self.selections.disjoint_anchors(),
10328 select_next_state: self.select_next_state.clone(),
10329 select_prev_state: self.select_prev_state.clone(),
10330 add_selections_state: self.add_selections_state.clone(),
10331 });
10332 }
10333
10334 pub fn transact(
10335 &mut self,
10336 cx: &mut ViewContext<Self>,
10337 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10338 ) -> Option<TransactionId> {
10339 self.start_transaction_at(Instant::now(), cx);
10340 update(self, cx);
10341 self.end_transaction_at(Instant::now(), cx)
10342 }
10343
10344 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10345 self.end_selection(cx);
10346 if let Some(tx_id) = self
10347 .buffer
10348 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10349 {
10350 self.selection_history
10351 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10352 cx.emit(EditorEvent::TransactionBegun {
10353 transaction_id: tx_id,
10354 })
10355 }
10356 }
10357
10358 fn end_transaction_at(
10359 &mut self,
10360 now: Instant,
10361 cx: &mut ViewContext<Self>,
10362 ) -> Option<TransactionId> {
10363 if let Some(transaction_id) = self
10364 .buffer
10365 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10366 {
10367 if let Some((_, end_selections)) =
10368 self.selection_history.transaction_mut(transaction_id)
10369 {
10370 *end_selections = Some(self.selections.disjoint_anchors());
10371 } else {
10372 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10373 }
10374
10375 cx.emit(EditorEvent::Edited { transaction_id });
10376 Some(transaction_id)
10377 } else {
10378 None
10379 }
10380 }
10381
10382 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10383 let mut fold_ranges = Vec::new();
10384
10385 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10386
10387 let selections = self.selections.all_adjusted(cx);
10388 for selection in selections {
10389 let range = selection.range().sorted();
10390 let buffer_start_row = range.start.row;
10391
10392 for row in (0..=range.end.row).rev() {
10393 if let Some((foldable_range, fold_text)) =
10394 display_map.foldable_range(MultiBufferRow(row))
10395 {
10396 if foldable_range.end.row >= buffer_start_row {
10397 fold_ranges.push((foldable_range, fold_text));
10398 if row <= range.start.row {
10399 break;
10400 }
10401 }
10402 }
10403 }
10404 }
10405
10406 self.fold_ranges(fold_ranges, true, cx);
10407 }
10408
10409 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10410 let buffer_row = fold_at.buffer_row;
10411 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10412
10413 if let Some((fold_range, placeholder)) = display_map.foldable_range(buffer_row) {
10414 let autoscroll = self
10415 .selections
10416 .all::<Point>(cx)
10417 .iter()
10418 .any(|selection| fold_range.overlaps(&selection.range()));
10419
10420 self.fold_ranges([(fold_range, placeholder)], autoscroll, cx);
10421 }
10422 }
10423
10424 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10425 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10426 let buffer = &display_map.buffer_snapshot;
10427 let selections = self.selections.all::<Point>(cx);
10428 let ranges = selections
10429 .iter()
10430 .map(|s| {
10431 let range = s.display_range(&display_map).sorted();
10432 let mut start = range.start.to_point(&display_map);
10433 let mut end = range.end.to_point(&display_map);
10434 start.column = 0;
10435 end.column = buffer.line_len(MultiBufferRow(end.row));
10436 start..end
10437 })
10438 .collect::<Vec<_>>();
10439
10440 self.unfold_ranges(ranges, true, true, cx);
10441 }
10442
10443 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10444 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10445
10446 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10447 ..Point::new(
10448 unfold_at.buffer_row.0,
10449 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10450 );
10451
10452 let autoscroll = self
10453 .selections
10454 .all::<Point>(cx)
10455 .iter()
10456 .any(|selection| selection.range().overlaps(&intersection_range));
10457
10458 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
10459 }
10460
10461 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10462 let selections = self.selections.all::<Point>(cx);
10463 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10464 let line_mode = self.selections.line_mode;
10465 let ranges = selections.into_iter().map(|s| {
10466 if line_mode {
10467 let start = Point::new(s.start.row, 0);
10468 let end = Point::new(
10469 s.end.row,
10470 display_map
10471 .buffer_snapshot
10472 .line_len(MultiBufferRow(s.end.row)),
10473 );
10474 (start..end, display_map.fold_placeholder.clone())
10475 } else {
10476 (s.start..s.end, display_map.fold_placeholder.clone())
10477 }
10478 });
10479 self.fold_ranges(ranges, true, cx);
10480 }
10481
10482 pub fn fold_ranges<T: ToOffset + Clone>(
10483 &mut self,
10484 ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
10485 auto_scroll: bool,
10486 cx: &mut ViewContext<Self>,
10487 ) {
10488 let mut fold_ranges = Vec::new();
10489 let mut buffers_affected = HashMap::default();
10490 let multi_buffer = self.buffer().read(cx);
10491 for (fold_range, fold_text) in ranges {
10492 if let Some((_, buffer, _)) =
10493 multi_buffer.excerpt_containing(fold_range.start.clone(), cx)
10494 {
10495 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10496 };
10497 fold_ranges.push((fold_range, fold_text));
10498 }
10499
10500 let mut ranges = fold_ranges.into_iter().peekable();
10501 if ranges.peek().is_some() {
10502 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
10503
10504 if auto_scroll {
10505 self.request_autoscroll(Autoscroll::fit(), cx);
10506 }
10507
10508 for buffer in buffers_affected.into_values() {
10509 self.sync_expanded_diff_hunks(buffer, cx);
10510 }
10511
10512 cx.notify();
10513
10514 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10515 // Clear diagnostics block when folding a range that contains it.
10516 let snapshot = self.snapshot(cx);
10517 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10518 drop(snapshot);
10519 self.active_diagnostics = Some(active_diagnostics);
10520 self.dismiss_diagnostics(cx);
10521 } else {
10522 self.active_diagnostics = Some(active_diagnostics);
10523 }
10524 }
10525
10526 self.scrollbar_marker_state.dirty = true;
10527 }
10528 }
10529
10530 pub fn unfold_ranges<T: ToOffset + Clone>(
10531 &mut self,
10532 ranges: impl IntoIterator<Item = Range<T>>,
10533 inclusive: bool,
10534 auto_scroll: bool,
10535 cx: &mut ViewContext<Self>,
10536 ) {
10537 let mut unfold_ranges = Vec::new();
10538 let mut buffers_affected = HashMap::default();
10539 let multi_buffer = self.buffer().read(cx);
10540 for range in ranges {
10541 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
10542 buffers_affected.insert(buffer.read(cx).remote_id(), buffer);
10543 };
10544 unfold_ranges.push(range);
10545 }
10546
10547 let mut ranges = unfold_ranges.into_iter().peekable();
10548 if ranges.peek().is_some() {
10549 self.display_map
10550 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
10551 if auto_scroll {
10552 self.request_autoscroll(Autoscroll::fit(), cx);
10553 }
10554
10555 for buffer in buffers_affected.into_values() {
10556 self.sync_expanded_diff_hunks(buffer, cx);
10557 }
10558
10559 cx.notify();
10560 self.scrollbar_marker_state.dirty = true;
10561 self.active_indent_guides_state.dirty = true;
10562 }
10563 }
10564
10565 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
10566 self.display_map.read(cx).fold_placeholder.clone()
10567 }
10568
10569 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
10570 if hovered != self.gutter_hovered {
10571 self.gutter_hovered = hovered;
10572 cx.notify();
10573 }
10574 }
10575
10576 pub fn insert_blocks(
10577 &mut self,
10578 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
10579 autoscroll: Option<Autoscroll>,
10580 cx: &mut ViewContext<Self>,
10581 ) -> Vec<CustomBlockId> {
10582 let blocks = self
10583 .display_map
10584 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
10585 if let Some(autoscroll) = autoscroll {
10586 self.request_autoscroll(autoscroll, cx);
10587 }
10588 cx.notify();
10589 blocks
10590 }
10591
10592 pub fn resize_blocks(
10593 &mut self,
10594 heights: HashMap<CustomBlockId, u32>,
10595 autoscroll: Option<Autoscroll>,
10596 cx: &mut ViewContext<Self>,
10597 ) {
10598 self.display_map
10599 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
10600 if let Some(autoscroll) = autoscroll {
10601 self.request_autoscroll(autoscroll, cx);
10602 }
10603 cx.notify();
10604 }
10605
10606 pub fn replace_blocks(
10607 &mut self,
10608 renderers: HashMap<CustomBlockId, RenderBlock>,
10609 autoscroll: Option<Autoscroll>,
10610 cx: &mut ViewContext<Self>,
10611 ) {
10612 self.display_map
10613 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
10614 if let Some(autoscroll) = autoscroll {
10615 self.request_autoscroll(autoscroll, cx);
10616 }
10617 cx.notify();
10618 }
10619
10620 pub fn remove_blocks(
10621 &mut self,
10622 block_ids: HashSet<CustomBlockId>,
10623 autoscroll: Option<Autoscroll>,
10624 cx: &mut ViewContext<Self>,
10625 ) {
10626 self.display_map.update(cx, |display_map, cx| {
10627 display_map.remove_blocks(block_ids, cx)
10628 });
10629 if let Some(autoscroll) = autoscroll {
10630 self.request_autoscroll(autoscroll, cx);
10631 }
10632 cx.notify();
10633 }
10634
10635 pub fn row_for_block(
10636 &self,
10637 block_id: CustomBlockId,
10638 cx: &mut ViewContext<Self>,
10639 ) -> Option<DisplayRow> {
10640 self.display_map
10641 .update(cx, |map, cx| map.row_for_block(block_id, cx))
10642 }
10643
10644 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
10645 self.focused_block = Some(focused_block);
10646 }
10647
10648 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
10649 self.focused_block.take()
10650 }
10651
10652 pub fn insert_creases(
10653 &mut self,
10654 creases: impl IntoIterator<Item = Crease>,
10655 cx: &mut ViewContext<Self>,
10656 ) -> Vec<CreaseId> {
10657 self.display_map
10658 .update(cx, |map, cx| map.insert_creases(creases, cx))
10659 }
10660
10661 pub fn remove_creases(
10662 &mut self,
10663 ids: impl IntoIterator<Item = CreaseId>,
10664 cx: &mut ViewContext<Self>,
10665 ) {
10666 self.display_map
10667 .update(cx, |map, cx| map.remove_creases(ids, cx));
10668 }
10669
10670 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
10671 self.display_map
10672 .update(cx, |map, cx| map.snapshot(cx))
10673 .longest_row()
10674 }
10675
10676 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
10677 self.display_map
10678 .update(cx, |map, cx| map.snapshot(cx))
10679 .max_point()
10680 }
10681
10682 pub fn text(&self, cx: &AppContext) -> String {
10683 self.buffer.read(cx).read(cx).text()
10684 }
10685
10686 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
10687 let text = self.text(cx);
10688 let text = text.trim();
10689
10690 if text.is_empty() {
10691 return None;
10692 }
10693
10694 Some(text.to_string())
10695 }
10696
10697 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
10698 self.transact(cx, |this, cx| {
10699 this.buffer
10700 .read(cx)
10701 .as_singleton()
10702 .expect("you can only call set_text on editors for singleton buffers")
10703 .update(cx, |buffer, cx| buffer.set_text(text, cx));
10704 });
10705 }
10706
10707 pub fn display_text(&self, cx: &mut AppContext) -> String {
10708 self.display_map
10709 .update(cx, |map, cx| map.snapshot(cx))
10710 .text()
10711 }
10712
10713 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
10714 let mut wrap_guides = smallvec::smallvec![];
10715
10716 if self.show_wrap_guides == Some(false) {
10717 return wrap_guides;
10718 }
10719
10720 let settings = self.buffer.read(cx).settings_at(0, cx);
10721 if settings.show_wrap_guides {
10722 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
10723 wrap_guides.push((soft_wrap as usize, true));
10724 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
10725 wrap_guides.push((soft_wrap as usize, true));
10726 }
10727 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
10728 }
10729
10730 wrap_guides
10731 }
10732
10733 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
10734 let settings = self.buffer.read(cx).settings_at(0, cx);
10735 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
10736 match mode {
10737 language_settings::SoftWrap::None => SoftWrap::None,
10738 language_settings::SoftWrap::PreferLine => SoftWrap::PreferLine,
10739 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
10740 language_settings::SoftWrap::PreferredLineLength => {
10741 SoftWrap::Column(settings.preferred_line_length)
10742 }
10743 language_settings::SoftWrap::Bounded => {
10744 SoftWrap::Bounded(settings.preferred_line_length)
10745 }
10746 }
10747 }
10748
10749 pub fn set_soft_wrap_mode(
10750 &mut self,
10751 mode: language_settings::SoftWrap,
10752 cx: &mut ViewContext<Self>,
10753 ) {
10754 self.soft_wrap_mode_override = Some(mode);
10755 cx.notify();
10756 }
10757
10758 pub fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
10759 let rem_size = cx.rem_size();
10760 self.display_map.update(cx, |map, cx| {
10761 map.set_font(
10762 style.text.font(),
10763 style.text.font_size.to_pixels(rem_size),
10764 cx,
10765 )
10766 });
10767 self.style = Some(style);
10768 }
10769
10770 pub fn style(&self) -> Option<&EditorStyle> {
10771 self.style.as_ref()
10772 }
10773
10774 // Called by the element. This method is not designed to be called outside of the editor
10775 // element's layout code because it does not notify when rewrapping is computed synchronously.
10776 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
10777 self.display_map
10778 .update(cx, |map, cx| map.set_wrap_width(width, cx))
10779 }
10780
10781 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
10782 if self.soft_wrap_mode_override.is_some() {
10783 self.soft_wrap_mode_override.take();
10784 } else {
10785 let soft_wrap = match self.soft_wrap_mode(cx) {
10786 SoftWrap::None | SoftWrap::PreferLine => language_settings::SoftWrap::EditorWidth,
10787 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
10788 language_settings::SoftWrap::PreferLine
10789 }
10790 };
10791 self.soft_wrap_mode_override = Some(soft_wrap);
10792 }
10793 cx.notify();
10794 }
10795
10796 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
10797 let Some(workspace) = self.workspace() else {
10798 return;
10799 };
10800 let fs = workspace.read(cx).app_state().fs.clone();
10801 let current_show = TabBarSettings::get_global(cx).show;
10802 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
10803 setting.show = Some(!current_show);
10804 });
10805 }
10806
10807 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
10808 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
10809 self.buffer
10810 .read(cx)
10811 .settings_at(0, cx)
10812 .indent_guides
10813 .enabled
10814 });
10815 self.show_indent_guides = Some(!currently_enabled);
10816 cx.notify();
10817 }
10818
10819 fn should_show_indent_guides(&self) -> Option<bool> {
10820 self.show_indent_guides
10821 }
10822
10823 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
10824 let mut editor_settings = EditorSettings::get_global(cx).clone();
10825 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
10826 EditorSettings::override_global(editor_settings, cx);
10827 }
10828
10829 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
10830 self.use_relative_line_numbers
10831 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
10832 }
10833
10834 pub fn toggle_relative_line_numbers(
10835 &mut self,
10836 _: &ToggleRelativeLineNumbers,
10837 cx: &mut ViewContext<Self>,
10838 ) {
10839 let is_relative = self.should_use_relative_line_numbers(cx);
10840 self.set_relative_line_number(Some(!is_relative), cx)
10841 }
10842
10843 pub fn set_relative_line_number(
10844 &mut self,
10845 is_relative: Option<bool>,
10846 cx: &mut ViewContext<Self>,
10847 ) {
10848 self.use_relative_line_numbers = is_relative;
10849 cx.notify();
10850 }
10851
10852 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
10853 self.show_gutter = show_gutter;
10854 cx.notify();
10855 }
10856
10857 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
10858 self.show_line_numbers = Some(show_line_numbers);
10859 cx.notify();
10860 }
10861
10862 pub fn set_show_git_diff_gutter(
10863 &mut self,
10864 show_git_diff_gutter: bool,
10865 cx: &mut ViewContext<Self>,
10866 ) {
10867 self.show_git_diff_gutter = Some(show_git_diff_gutter);
10868 cx.notify();
10869 }
10870
10871 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
10872 self.show_code_actions = Some(show_code_actions);
10873 cx.notify();
10874 }
10875
10876 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
10877 self.show_runnables = Some(show_runnables);
10878 cx.notify();
10879 }
10880
10881 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
10882 if self.display_map.read(cx).masked != masked {
10883 self.display_map.update(cx, |map, _| map.masked = masked);
10884 }
10885 cx.notify()
10886 }
10887
10888 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
10889 self.show_wrap_guides = Some(show_wrap_guides);
10890 cx.notify();
10891 }
10892
10893 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
10894 self.show_indent_guides = Some(show_indent_guides);
10895 cx.notify();
10896 }
10897
10898 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
10899 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10900 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10901 if let Some(dir) = file.abs_path(cx).parent() {
10902 return Some(dir.to_owned());
10903 }
10904 }
10905
10906 if let Some(project_path) = buffer.read(cx).project_path(cx) {
10907 return Some(project_path.path.to_path_buf());
10908 }
10909 }
10910
10911 None
10912 }
10913
10914 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
10915 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10916 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10917 cx.reveal_path(&file.abs_path(cx));
10918 }
10919 }
10920 }
10921
10922 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
10923 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10924 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10925 if let Some(path) = file.abs_path(cx).to_str() {
10926 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10927 }
10928 }
10929 }
10930 }
10931
10932 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
10933 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
10934 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
10935 if let Some(path) = file.path().to_str() {
10936 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
10937 }
10938 }
10939 }
10940 }
10941
10942 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
10943 self.show_git_blame_gutter = !self.show_git_blame_gutter;
10944
10945 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
10946 self.start_git_blame(true, cx);
10947 }
10948
10949 cx.notify();
10950 }
10951
10952 pub fn toggle_git_blame_inline(
10953 &mut self,
10954 _: &ToggleGitBlameInline,
10955 cx: &mut ViewContext<Self>,
10956 ) {
10957 self.toggle_git_blame_inline_internal(true, cx);
10958 cx.notify();
10959 }
10960
10961 pub fn git_blame_inline_enabled(&self) -> bool {
10962 self.git_blame_inline_enabled
10963 }
10964
10965 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
10966 self.show_selection_menu = self
10967 .show_selection_menu
10968 .map(|show_selections_menu| !show_selections_menu)
10969 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
10970
10971 cx.notify();
10972 }
10973
10974 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
10975 self.show_selection_menu
10976 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
10977 }
10978
10979 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
10980 if let Some(project) = self.project.as_ref() {
10981 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
10982 return;
10983 };
10984
10985 if buffer.read(cx).file().is_none() {
10986 return;
10987 }
10988
10989 let focused = self.focus_handle(cx).contains_focused(cx);
10990
10991 let project = project.clone();
10992 let blame =
10993 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
10994 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
10995 self.blame = Some(blame);
10996 }
10997 }
10998
10999 fn toggle_git_blame_inline_internal(
11000 &mut self,
11001 user_triggered: bool,
11002 cx: &mut ViewContext<Self>,
11003 ) {
11004 if self.git_blame_inline_enabled {
11005 self.git_blame_inline_enabled = false;
11006 self.show_git_blame_inline = false;
11007 self.show_git_blame_inline_delay_task.take();
11008 } else {
11009 self.git_blame_inline_enabled = true;
11010 self.start_git_blame_inline(user_triggered, cx);
11011 }
11012
11013 cx.notify();
11014 }
11015
11016 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11017 self.start_git_blame(user_triggered, cx);
11018
11019 if ProjectSettings::get_global(cx)
11020 .git
11021 .inline_blame_delay()
11022 .is_some()
11023 {
11024 self.start_inline_blame_timer(cx);
11025 } else {
11026 self.show_git_blame_inline = true
11027 }
11028 }
11029
11030 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11031 self.blame.as_ref()
11032 }
11033
11034 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11035 self.show_git_blame_gutter && self.has_blame_entries(cx)
11036 }
11037
11038 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11039 self.show_git_blame_inline
11040 && self.focus_handle.is_focused(cx)
11041 && !self.newest_selection_head_on_empty_line(cx)
11042 && self.has_blame_entries(cx)
11043 }
11044
11045 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11046 self.blame()
11047 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11048 }
11049
11050 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11051 let cursor_anchor = self.selections.newest_anchor().head();
11052
11053 let snapshot = self.buffer.read(cx).snapshot(cx);
11054 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11055
11056 snapshot.line_len(buffer_row) == 0
11057 }
11058
11059 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Result<url::Url> {
11060 let (path, selection, repo) = maybe!({
11061 let project_handle = self.project.as_ref()?.clone();
11062 let project = project_handle.read(cx);
11063
11064 let selection = self.selections.newest::<Point>(cx);
11065 let selection_range = selection.range();
11066
11067 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11068 (buffer, selection_range.start.row..selection_range.end.row)
11069 } else {
11070 let buffer_ranges = self
11071 .buffer()
11072 .read(cx)
11073 .range_to_buffer_ranges(selection_range, cx);
11074
11075 let (buffer, range, _) = if selection.reversed {
11076 buffer_ranges.first()
11077 } else {
11078 buffer_ranges.last()
11079 }?;
11080
11081 let snapshot = buffer.read(cx).snapshot();
11082 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11083 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11084 (buffer.clone(), selection)
11085 };
11086
11087 let path = buffer
11088 .read(cx)
11089 .file()?
11090 .as_local()?
11091 .path()
11092 .to_str()?
11093 .to_string();
11094 let repo = project.get_repo(&buffer.read(cx).project_path(cx)?, cx)?;
11095 Some((path, selection, repo))
11096 })
11097 .ok_or_else(|| anyhow!("unable to open git repository"))?;
11098
11099 const REMOTE_NAME: &str = "origin";
11100 let origin_url = repo
11101 .remote_url(REMOTE_NAME)
11102 .ok_or_else(|| anyhow!("remote \"{REMOTE_NAME}\" not found"))?;
11103 let sha = repo
11104 .head_sha()
11105 .ok_or_else(|| anyhow!("failed to read HEAD SHA"))?;
11106
11107 let (provider, remote) =
11108 parse_git_remote_url(GitHostingProviderRegistry::default_global(cx), &origin_url)
11109 .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
11110
11111 Ok(provider.build_permalink(
11112 remote,
11113 BuildPermalinkParams {
11114 sha: &sha,
11115 path: &path,
11116 selection: Some(selection),
11117 },
11118 ))
11119 }
11120
11121 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11122 let permalink = self.get_permalink_to_line(cx);
11123
11124 match permalink {
11125 Ok(permalink) => {
11126 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11127 }
11128 Err(err) => {
11129 let message = format!("Failed to copy permalink: {err}");
11130
11131 Err::<(), anyhow::Error>(err).log_err();
11132
11133 if let Some(workspace) = self.workspace() {
11134 workspace.update(cx, |workspace, cx| {
11135 struct CopyPermalinkToLine;
11136
11137 workspace.show_toast(
11138 Toast::new(NotificationId::unique::<CopyPermalinkToLine>(), message),
11139 cx,
11140 )
11141 })
11142 }
11143 }
11144 }
11145 }
11146
11147 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11148 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11149 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11150 if let Some(path) = file.path().to_str() {
11151 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11152 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11153 }
11154 }
11155 }
11156 }
11157
11158 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11159 let permalink = self.get_permalink_to_line(cx);
11160
11161 match permalink {
11162 Ok(permalink) => {
11163 cx.open_url(permalink.as_ref());
11164 }
11165 Err(err) => {
11166 let message = format!("Failed to open permalink: {err}");
11167
11168 Err::<(), anyhow::Error>(err).log_err();
11169
11170 if let Some(workspace) = self.workspace() {
11171 workspace.update(cx, |workspace, cx| {
11172 struct OpenPermalinkToLine;
11173
11174 workspace.show_toast(
11175 Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
11176 cx,
11177 )
11178 })
11179 }
11180 }
11181 }
11182 }
11183
11184 /// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
11185 /// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
11186 /// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
11187 pub fn highlight_rows<T: 'static>(
11188 &mut self,
11189 rows: RangeInclusive<Anchor>,
11190 color: Option<Hsla>,
11191 should_autoscroll: bool,
11192 cx: &mut ViewContext<Self>,
11193 ) {
11194 let snapshot = self.buffer().read(cx).snapshot(cx);
11195 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11196 let existing_highlight_index = row_highlights.binary_search_by(|highlight| {
11197 highlight
11198 .range
11199 .start()
11200 .cmp(rows.start(), &snapshot)
11201 .then(highlight.range.end().cmp(rows.end(), &snapshot))
11202 });
11203 match (color, existing_highlight_index) {
11204 (Some(_), Ok(ix)) | (_, Err(ix)) => row_highlights.insert(
11205 ix,
11206 RowHighlight {
11207 index: post_inc(&mut self.highlight_order),
11208 range: rows,
11209 should_autoscroll,
11210 color,
11211 },
11212 ),
11213 (None, Ok(i)) => {
11214 row_highlights.remove(i);
11215 }
11216 }
11217 }
11218
11219 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11220 pub fn clear_row_highlights<T: 'static>(&mut self) {
11221 self.highlighted_rows.remove(&TypeId::of::<T>());
11222 }
11223
11224 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11225 pub fn highlighted_rows<T: 'static>(
11226 &self,
11227 ) -> Option<impl Iterator<Item = (&RangeInclusive<Anchor>, Option<&Hsla>)>> {
11228 Some(
11229 self.highlighted_rows
11230 .get(&TypeId::of::<T>())?
11231 .iter()
11232 .map(|highlight| (&highlight.range, highlight.color.as_ref())),
11233 )
11234 }
11235
11236 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11237 /// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
11238 /// Allows to ignore certain kinds of highlights.
11239 pub fn highlighted_display_rows(
11240 &mut self,
11241 cx: &mut WindowContext,
11242 ) -> BTreeMap<DisplayRow, Hsla> {
11243 let snapshot = self.snapshot(cx);
11244 let mut used_highlight_orders = HashMap::default();
11245 self.highlighted_rows
11246 .iter()
11247 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11248 .fold(
11249 BTreeMap::<DisplayRow, Hsla>::new(),
11250 |mut unique_rows, highlight| {
11251 let start_row = highlight.range.start().to_display_point(&snapshot).row();
11252 let end_row = highlight.range.end().to_display_point(&snapshot).row();
11253 for row in start_row.0..=end_row.0 {
11254 let used_index =
11255 used_highlight_orders.entry(row).or_insert(highlight.index);
11256 if highlight.index >= *used_index {
11257 *used_index = highlight.index;
11258 match highlight.color {
11259 Some(hsla) => unique_rows.insert(DisplayRow(row), hsla),
11260 None => unique_rows.remove(&DisplayRow(row)),
11261 };
11262 }
11263 }
11264 unique_rows
11265 },
11266 )
11267 }
11268
11269 pub fn highlighted_display_row_for_autoscroll(
11270 &self,
11271 snapshot: &DisplaySnapshot,
11272 ) -> Option<DisplayRow> {
11273 self.highlighted_rows
11274 .values()
11275 .flat_map(|highlighted_rows| highlighted_rows.iter())
11276 .filter_map(|highlight| {
11277 if highlight.color.is_none() || !highlight.should_autoscroll {
11278 return None;
11279 }
11280 Some(highlight.range.start().to_display_point(snapshot).row())
11281 })
11282 .min()
11283 }
11284
11285 pub fn set_search_within_ranges(
11286 &mut self,
11287 ranges: &[Range<Anchor>],
11288 cx: &mut ViewContext<Self>,
11289 ) {
11290 self.highlight_background::<SearchWithinRange>(
11291 ranges,
11292 |colors| colors.editor_document_highlight_read_background,
11293 cx,
11294 )
11295 }
11296
11297 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11298 self.breadcrumb_header = Some(new_header);
11299 }
11300
11301 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11302 self.clear_background_highlights::<SearchWithinRange>(cx);
11303 }
11304
11305 pub fn highlight_background<T: 'static>(
11306 &mut self,
11307 ranges: &[Range<Anchor>],
11308 color_fetcher: fn(&ThemeColors) -> Hsla,
11309 cx: &mut ViewContext<Self>,
11310 ) {
11311 self.background_highlights
11312 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11313 self.scrollbar_marker_state.dirty = true;
11314 cx.notify();
11315 }
11316
11317 pub fn clear_background_highlights<T: 'static>(
11318 &mut self,
11319 cx: &mut ViewContext<Self>,
11320 ) -> Option<BackgroundHighlight> {
11321 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11322 if !text_highlights.1.is_empty() {
11323 self.scrollbar_marker_state.dirty = true;
11324 cx.notify();
11325 }
11326 Some(text_highlights)
11327 }
11328
11329 pub fn highlight_gutter<T: 'static>(
11330 &mut self,
11331 ranges: &[Range<Anchor>],
11332 color_fetcher: fn(&AppContext) -> Hsla,
11333 cx: &mut ViewContext<Self>,
11334 ) {
11335 self.gutter_highlights
11336 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11337 cx.notify();
11338 }
11339
11340 pub fn clear_gutter_highlights<T: 'static>(
11341 &mut self,
11342 cx: &mut ViewContext<Self>,
11343 ) -> Option<GutterHighlight> {
11344 cx.notify();
11345 self.gutter_highlights.remove(&TypeId::of::<T>())
11346 }
11347
11348 #[cfg(feature = "test-support")]
11349 pub fn all_text_background_highlights(
11350 &mut self,
11351 cx: &mut ViewContext<Self>,
11352 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11353 let snapshot = self.snapshot(cx);
11354 let buffer = &snapshot.buffer_snapshot;
11355 let start = buffer.anchor_before(0);
11356 let end = buffer.anchor_after(buffer.len());
11357 let theme = cx.theme().colors();
11358 self.background_highlights_in_range(start..end, &snapshot, theme)
11359 }
11360
11361 #[cfg(feature = "test-support")]
11362 pub fn search_background_highlights(
11363 &mut self,
11364 cx: &mut ViewContext<Self>,
11365 ) -> Vec<Range<Point>> {
11366 let snapshot = self.buffer().read(cx).snapshot(cx);
11367
11368 let highlights = self
11369 .background_highlights
11370 .get(&TypeId::of::<items::BufferSearchHighlights>());
11371
11372 if let Some((_color, ranges)) = highlights {
11373 ranges
11374 .iter()
11375 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
11376 .collect_vec()
11377 } else {
11378 vec![]
11379 }
11380 }
11381
11382 fn document_highlights_for_position<'a>(
11383 &'a self,
11384 position: Anchor,
11385 buffer: &'a MultiBufferSnapshot,
11386 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
11387 let read_highlights = self
11388 .background_highlights
11389 .get(&TypeId::of::<DocumentHighlightRead>())
11390 .map(|h| &h.1);
11391 let write_highlights = self
11392 .background_highlights
11393 .get(&TypeId::of::<DocumentHighlightWrite>())
11394 .map(|h| &h.1);
11395 let left_position = position.bias_left(buffer);
11396 let right_position = position.bias_right(buffer);
11397 read_highlights
11398 .into_iter()
11399 .chain(write_highlights)
11400 .flat_map(move |ranges| {
11401 let start_ix = match ranges.binary_search_by(|probe| {
11402 let cmp = probe.end.cmp(&left_position, buffer);
11403 if cmp.is_ge() {
11404 Ordering::Greater
11405 } else {
11406 Ordering::Less
11407 }
11408 }) {
11409 Ok(i) | Err(i) => i,
11410 };
11411
11412 ranges[start_ix..]
11413 .iter()
11414 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
11415 })
11416 }
11417
11418 pub fn has_background_highlights<T: 'static>(&self) -> bool {
11419 self.background_highlights
11420 .get(&TypeId::of::<T>())
11421 .map_or(false, |(_, highlights)| !highlights.is_empty())
11422 }
11423
11424 pub fn background_highlights_in_range(
11425 &self,
11426 search_range: Range<Anchor>,
11427 display_snapshot: &DisplaySnapshot,
11428 theme: &ThemeColors,
11429 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11430 let mut results = Vec::new();
11431 for (color_fetcher, ranges) in self.background_highlights.values() {
11432 let color = color_fetcher(theme);
11433 let start_ix = match ranges.binary_search_by(|probe| {
11434 let cmp = probe
11435 .end
11436 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11437 if cmp.is_gt() {
11438 Ordering::Greater
11439 } else {
11440 Ordering::Less
11441 }
11442 }) {
11443 Ok(i) | Err(i) => i,
11444 };
11445 for range in &ranges[start_ix..] {
11446 if range
11447 .start
11448 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11449 .is_ge()
11450 {
11451 break;
11452 }
11453
11454 let start = range.start.to_display_point(display_snapshot);
11455 let end = range.end.to_display_point(display_snapshot);
11456 results.push((start..end, color))
11457 }
11458 }
11459 results
11460 }
11461
11462 pub fn background_highlight_row_ranges<T: 'static>(
11463 &self,
11464 search_range: Range<Anchor>,
11465 display_snapshot: &DisplaySnapshot,
11466 count: usize,
11467 ) -> Vec<RangeInclusive<DisplayPoint>> {
11468 let mut results = Vec::new();
11469 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
11470 return vec![];
11471 };
11472
11473 let start_ix = match ranges.binary_search_by(|probe| {
11474 let cmp = probe
11475 .end
11476 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11477 if cmp.is_gt() {
11478 Ordering::Greater
11479 } else {
11480 Ordering::Less
11481 }
11482 }) {
11483 Ok(i) | Err(i) => i,
11484 };
11485 let mut push_region = |start: Option<Point>, end: Option<Point>| {
11486 if let (Some(start_display), Some(end_display)) = (start, end) {
11487 results.push(
11488 start_display.to_display_point(display_snapshot)
11489 ..=end_display.to_display_point(display_snapshot),
11490 );
11491 }
11492 };
11493 let mut start_row: Option<Point> = None;
11494 let mut end_row: Option<Point> = None;
11495 if ranges.len() > count {
11496 return Vec::new();
11497 }
11498 for range in &ranges[start_ix..] {
11499 if range
11500 .start
11501 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11502 .is_ge()
11503 {
11504 break;
11505 }
11506 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
11507 if let Some(current_row) = &end_row {
11508 if end.row == current_row.row {
11509 continue;
11510 }
11511 }
11512 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
11513 if start_row.is_none() {
11514 assert_eq!(end_row, None);
11515 start_row = Some(start);
11516 end_row = Some(end);
11517 continue;
11518 }
11519 if let Some(current_end) = end_row.as_mut() {
11520 if start.row > current_end.row + 1 {
11521 push_region(start_row, end_row);
11522 start_row = Some(start);
11523 end_row = Some(end);
11524 } else {
11525 // Merge two hunks.
11526 *current_end = end;
11527 }
11528 } else {
11529 unreachable!();
11530 }
11531 }
11532 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
11533 push_region(start_row, end_row);
11534 results
11535 }
11536
11537 pub fn gutter_highlights_in_range(
11538 &self,
11539 search_range: Range<Anchor>,
11540 display_snapshot: &DisplaySnapshot,
11541 cx: &AppContext,
11542 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
11543 let mut results = Vec::new();
11544 for (color_fetcher, ranges) in self.gutter_highlights.values() {
11545 let color = color_fetcher(cx);
11546 let start_ix = match ranges.binary_search_by(|probe| {
11547 let cmp = probe
11548 .end
11549 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
11550 if cmp.is_gt() {
11551 Ordering::Greater
11552 } else {
11553 Ordering::Less
11554 }
11555 }) {
11556 Ok(i) | Err(i) => i,
11557 };
11558 for range in &ranges[start_ix..] {
11559 if range
11560 .start
11561 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
11562 .is_ge()
11563 {
11564 break;
11565 }
11566
11567 let start = range.start.to_display_point(display_snapshot);
11568 let end = range.end.to_display_point(display_snapshot);
11569 results.push((start..end, color))
11570 }
11571 }
11572 results
11573 }
11574
11575 /// Get the text ranges corresponding to the redaction query
11576 pub fn redacted_ranges(
11577 &self,
11578 search_range: Range<Anchor>,
11579 display_snapshot: &DisplaySnapshot,
11580 cx: &WindowContext,
11581 ) -> Vec<Range<DisplayPoint>> {
11582 display_snapshot
11583 .buffer_snapshot
11584 .redacted_ranges(search_range, |file| {
11585 if let Some(file) = file {
11586 file.is_private()
11587 && EditorSettings::get(
11588 Some(SettingsLocation {
11589 worktree_id: file.worktree_id(cx),
11590 path: file.path().as_ref(),
11591 }),
11592 cx,
11593 )
11594 .redact_private_values
11595 } else {
11596 false
11597 }
11598 })
11599 .map(|range| {
11600 range.start.to_display_point(display_snapshot)
11601 ..range.end.to_display_point(display_snapshot)
11602 })
11603 .collect()
11604 }
11605
11606 pub fn highlight_text<T: 'static>(
11607 &mut self,
11608 ranges: Vec<Range<Anchor>>,
11609 style: HighlightStyle,
11610 cx: &mut ViewContext<Self>,
11611 ) {
11612 self.display_map.update(cx, |map, _| {
11613 map.highlight_text(TypeId::of::<T>(), ranges, style)
11614 });
11615 cx.notify();
11616 }
11617
11618 pub(crate) fn highlight_inlays<T: 'static>(
11619 &mut self,
11620 highlights: Vec<InlayHighlight>,
11621 style: HighlightStyle,
11622 cx: &mut ViewContext<Self>,
11623 ) {
11624 self.display_map.update(cx, |map, _| {
11625 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
11626 });
11627 cx.notify();
11628 }
11629
11630 pub fn text_highlights<'a, T: 'static>(
11631 &'a self,
11632 cx: &'a AppContext,
11633 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
11634 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
11635 }
11636
11637 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
11638 let cleared = self
11639 .display_map
11640 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
11641 if cleared {
11642 cx.notify();
11643 }
11644 }
11645
11646 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
11647 (self.read_only(cx) || self.blink_manager.read(cx).visible())
11648 && self.focus_handle.is_focused(cx)
11649 }
11650
11651 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
11652 self.show_cursor_when_unfocused = is_enabled;
11653 cx.notify();
11654 }
11655
11656 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
11657 cx.notify();
11658 }
11659
11660 fn on_buffer_event(
11661 &mut self,
11662 multibuffer: Model<MultiBuffer>,
11663 event: &multi_buffer::Event,
11664 cx: &mut ViewContext<Self>,
11665 ) {
11666 match event {
11667 multi_buffer::Event::Edited {
11668 singleton_buffer_edited,
11669 } => {
11670 self.scrollbar_marker_state.dirty = true;
11671 self.active_indent_guides_state.dirty = true;
11672 self.refresh_active_diagnostics(cx);
11673 self.refresh_code_actions(cx);
11674 if self.has_active_inline_completion(cx) {
11675 self.update_visible_inline_completion(cx);
11676 }
11677 cx.emit(EditorEvent::BufferEdited);
11678 cx.emit(SearchEvent::MatchesInvalidated);
11679 if *singleton_buffer_edited {
11680 if let Some(project) = &self.project {
11681 let project = project.read(cx);
11682 #[allow(clippy::mutable_key_type)]
11683 let languages_affected = multibuffer
11684 .read(cx)
11685 .all_buffers()
11686 .into_iter()
11687 .filter_map(|buffer| {
11688 let buffer = buffer.read(cx);
11689 let language = buffer.language()?;
11690 if project.is_local_or_ssh()
11691 && project.language_servers_for_buffer(buffer, cx).count() == 0
11692 {
11693 None
11694 } else {
11695 Some(language)
11696 }
11697 })
11698 .cloned()
11699 .collect::<HashSet<_>>();
11700 if !languages_affected.is_empty() {
11701 self.refresh_inlay_hints(
11702 InlayHintRefreshReason::BufferEdited(languages_affected),
11703 cx,
11704 );
11705 }
11706 }
11707 }
11708
11709 let Some(project) = &self.project else { return };
11710 let telemetry = project.read(cx).client().telemetry().clone();
11711 refresh_linked_ranges(self, cx);
11712 telemetry.log_edit_event("editor");
11713 }
11714 multi_buffer::Event::ExcerptsAdded {
11715 buffer,
11716 predecessor,
11717 excerpts,
11718 } => {
11719 self.tasks_update_task = Some(self.refresh_runnables(cx));
11720 cx.emit(EditorEvent::ExcerptsAdded {
11721 buffer: buffer.clone(),
11722 predecessor: *predecessor,
11723 excerpts: excerpts.clone(),
11724 });
11725 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
11726 }
11727 multi_buffer::Event::ExcerptsRemoved { ids } => {
11728 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
11729 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
11730 }
11731 multi_buffer::Event::ExcerptsEdited { ids } => {
11732 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
11733 }
11734 multi_buffer::Event::ExcerptsExpanded { ids } => {
11735 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
11736 }
11737 multi_buffer::Event::Reparsed(buffer_id) => {
11738 self.tasks_update_task = Some(self.refresh_runnables(cx));
11739
11740 cx.emit(EditorEvent::Reparsed(*buffer_id));
11741 }
11742 multi_buffer::Event::LanguageChanged(buffer_id) => {
11743 linked_editing_ranges::refresh_linked_ranges(self, cx);
11744 cx.emit(EditorEvent::Reparsed(*buffer_id));
11745 cx.notify();
11746 }
11747 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
11748 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
11749 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
11750 cx.emit(EditorEvent::TitleChanged)
11751 }
11752 multi_buffer::Event::DiffBaseChanged => {
11753 self.scrollbar_marker_state.dirty = true;
11754 cx.emit(EditorEvent::DiffBaseChanged);
11755 cx.notify();
11756 }
11757 multi_buffer::Event::DiffUpdated { buffer } => {
11758 self.sync_expanded_diff_hunks(buffer.clone(), cx);
11759 cx.notify();
11760 }
11761 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
11762 multi_buffer::Event::DiagnosticsUpdated => {
11763 self.refresh_active_diagnostics(cx);
11764 self.scrollbar_marker_state.dirty = true;
11765 cx.notify();
11766 }
11767 _ => {}
11768 };
11769 }
11770
11771 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
11772 cx.notify();
11773 }
11774
11775 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
11776 self.tasks_update_task = Some(self.refresh_runnables(cx));
11777 self.refresh_inline_completion(true, false, cx);
11778 self.refresh_inlay_hints(
11779 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
11780 self.selections.newest_anchor().head(),
11781 &self.buffer.read(cx).snapshot(cx),
11782 cx,
11783 )),
11784 cx,
11785 );
11786 let editor_settings = EditorSettings::get_global(cx);
11787 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
11788 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
11789
11790 let project_settings = ProjectSettings::get_global(cx);
11791 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
11792
11793 if self.mode == EditorMode::Full {
11794 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
11795 if self.git_blame_inline_enabled != inline_blame_enabled {
11796 self.toggle_git_blame_inline_internal(false, cx);
11797 }
11798 }
11799
11800 cx.notify();
11801 }
11802
11803 pub fn set_searchable(&mut self, searchable: bool) {
11804 self.searchable = searchable;
11805 }
11806
11807 pub fn searchable(&self) -> bool {
11808 self.searchable
11809 }
11810
11811 fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
11812 self.open_excerpts_common(true, cx)
11813 }
11814
11815 fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
11816 self.open_excerpts_common(false, cx)
11817 }
11818
11819 fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
11820 let buffer = self.buffer.read(cx);
11821 if buffer.is_singleton() {
11822 cx.propagate();
11823 return;
11824 }
11825
11826 let Some(workspace) = self.workspace() else {
11827 cx.propagate();
11828 return;
11829 };
11830
11831 let mut new_selections_by_buffer = HashMap::default();
11832 for selection in self.selections.all::<usize>(cx) {
11833 for (buffer, mut range, _) in
11834 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
11835 {
11836 if selection.reversed {
11837 mem::swap(&mut range.start, &mut range.end);
11838 }
11839 new_selections_by_buffer
11840 .entry(buffer)
11841 .or_insert(Vec::new())
11842 .push(range)
11843 }
11844 }
11845
11846 // We defer the pane interaction because we ourselves are a workspace item
11847 // and activating a new item causes the pane to call a method on us reentrantly,
11848 // which panics if we're on the stack.
11849 cx.window_context().defer(move |cx| {
11850 workspace.update(cx, |workspace, cx| {
11851 let pane = if split {
11852 workspace.adjacent_pane(cx)
11853 } else {
11854 workspace.active_pane().clone()
11855 };
11856
11857 for (buffer, ranges) in new_selections_by_buffer {
11858 let editor =
11859 workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
11860 editor.update(cx, |editor, cx| {
11861 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
11862 s.select_ranges(ranges);
11863 });
11864 });
11865 }
11866 })
11867 });
11868 }
11869
11870 fn jump(
11871 &mut self,
11872 path: ProjectPath,
11873 position: Point,
11874 anchor: language::Anchor,
11875 offset_from_top: u32,
11876 cx: &mut ViewContext<Self>,
11877 ) {
11878 let workspace = self.workspace();
11879 cx.spawn(|_, mut cx| async move {
11880 let workspace = workspace.ok_or_else(|| anyhow!("cannot jump without workspace"))?;
11881 let editor = workspace.update(&mut cx, |workspace, cx| {
11882 // Reset the preview item id before opening the new item
11883 workspace.active_pane().update(cx, |pane, cx| {
11884 pane.set_preview_item_id(None, cx);
11885 });
11886 workspace.open_path_preview(path, None, true, true, cx)
11887 })?;
11888 let editor = editor
11889 .await?
11890 .downcast::<Editor>()
11891 .ok_or_else(|| anyhow!("opened item was not an editor"))?
11892 .downgrade();
11893 editor.update(&mut cx, |editor, cx| {
11894 let buffer = editor
11895 .buffer()
11896 .read(cx)
11897 .as_singleton()
11898 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
11899 let buffer = buffer.read(cx);
11900 let cursor = if buffer.can_resolve(&anchor) {
11901 language::ToPoint::to_point(&anchor, buffer)
11902 } else {
11903 buffer.clip_point(position, Bias::Left)
11904 };
11905
11906 let nav_history = editor.nav_history.take();
11907 editor.change_selections(
11908 Some(Autoscroll::top_relative(offset_from_top as usize)),
11909 cx,
11910 |s| {
11911 s.select_ranges([cursor..cursor]);
11912 },
11913 );
11914 editor.nav_history = nav_history;
11915
11916 anyhow::Ok(())
11917 })??;
11918
11919 anyhow::Ok(())
11920 })
11921 .detach_and_log_err(cx);
11922 }
11923
11924 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
11925 let snapshot = self.buffer.read(cx).read(cx);
11926 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
11927 Some(
11928 ranges
11929 .iter()
11930 .map(move |range| {
11931 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
11932 })
11933 .collect(),
11934 )
11935 }
11936
11937 fn selection_replacement_ranges(
11938 &self,
11939 range: Range<OffsetUtf16>,
11940 cx: &AppContext,
11941 ) -> Vec<Range<OffsetUtf16>> {
11942 let selections = self.selections.all::<OffsetUtf16>(cx);
11943 let newest_selection = selections
11944 .iter()
11945 .max_by_key(|selection| selection.id)
11946 .unwrap();
11947 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
11948 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
11949 let snapshot = self.buffer.read(cx).read(cx);
11950 selections
11951 .into_iter()
11952 .map(|mut selection| {
11953 selection.start.0 =
11954 (selection.start.0 as isize).saturating_add(start_delta) as usize;
11955 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
11956 snapshot.clip_offset_utf16(selection.start, Bias::Left)
11957 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
11958 })
11959 .collect()
11960 }
11961
11962 fn report_editor_event(
11963 &self,
11964 operation: &'static str,
11965 file_extension: Option<String>,
11966 cx: &AppContext,
11967 ) {
11968 if cfg!(any(test, feature = "test-support")) {
11969 return;
11970 }
11971
11972 let Some(project) = &self.project else { return };
11973
11974 // If None, we are in a file without an extension
11975 let file = self
11976 .buffer
11977 .read(cx)
11978 .as_singleton()
11979 .and_then(|b| b.read(cx).file());
11980 let file_extension = file_extension.or(file
11981 .as_ref()
11982 .and_then(|file| Path::new(file.file_name(cx)).extension())
11983 .and_then(|e| e.to_str())
11984 .map(|a| a.to_string()));
11985
11986 let vim_mode = cx
11987 .global::<SettingsStore>()
11988 .raw_user_settings()
11989 .get("vim_mode")
11990 == Some(&serde_json::Value::Bool(true));
11991
11992 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
11993 == language::language_settings::InlineCompletionProvider::Copilot;
11994 let copilot_enabled_for_language = self
11995 .buffer
11996 .read(cx)
11997 .settings_at(0, cx)
11998 .show_inline_completions;
11999
12000 let telemetry = project.read(cx).client().telemetry().clone();
12001 telemetry.report_editor_event(
12002 file_extension,
12003 vim_mode,
12004 operation,
12005 copilot_enabled,
12006 copilot_enabled_for_language,
12007 )
12008 }
12009
12010 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12011 /// with each line being an array of {text, highlight} objects.
12012 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12013 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12014 return;
12015 };
12016
12017 #[derive(Serialize)]
12018 struct Chunk<'a> {
12019 text: String,
12020 highlight: Option<&'a str>,
12021 }
12022
12023 let snapshot = buffer.read(cx).snapshot();
12024 let range = self
12025 .selected_text_range(false, cx)
12026 .and_then(|selection| {
12027 if selection.range.is_empty() {
12028 None
12029 } else {
12030 Some(selection.range)
12031 }
12032 })
12033 .unwrap_or_else(|| 0..snapshot.len());
12034
12035 let chunks = snapshot.chunks(range, true);
12036 let mut lines = Vec::new();
12037 let mut line: VecDeque<Chunk> = VecDeque::new();
12038
12039 let Some(style) = self.style.as_ref() else {
12040 return;
12041 };
12042
12043 for chunk in chunks {
12044 let highlight = chunk
12045 .syntax_highlight_id
12046 .and_then(|id| id.name(&style.syntax));
12047 let mut chunk_lines = chunk.text.split('\n').peekable();
12048 while let Some(text) = chunk_lines.next() {
12049 let mut merged_with_last_token = false;
12050 if let Some(last_token) = line.back_mut() {
12051 if last_token.highlight == highlight {
12052 last_token.text.push_str(text);
12053 merged_with_last_token = true;
12054 }
12055 }
12056
12057 if !merged_with_last_token {
12058 line.push_back(Chunk {
12059 text: text.into(),
12060 highlight,
12061 });
12062 }
12063
12064 if chunk_lines.peek().is_some() {
12065 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12066 line.pop_front();
12067 }
12068 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12069 line.pop_back();
12070 }
12071
12072 lines.push(mem::take(&mut line));
12073 }
12074 }
12075 }
12076
12077 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12078 return;
12079 };
12080 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12081 }
12082
12083 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12084 &self.inlay_hint_cache
12085 }
12086
12087 pub fn replay_insert_event(
12088 &mut self,
12089 text: &str,
12090 relative_utf16_range: Option<Range<isize>>,
12091 cx: &mut ViewContext<Self>,
12092 ) {
12093 if !self.input_enabled {
12094 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12095 return;
12096 }
12097 if let Some(relative_utf16_range) = relative_utf16_range {
12098 let selections = self.selections.all::<OffsetUtf16>(cx);
12099 self.change_selections(None, cx, |s| {
12100 let new_ranges = selections.into_iter().map(|range| {
12101 let start = OffsetUtf16(
12102 range
12103 .head()
12104 .0
12105 .saturating_add_signed(relative_utf16_range.start),
12106 );
12107 let end = OffsetUtf16(
12108 range
12109 .head()
12110 .0
12111 .saturating_add_signed(relative_utf16_range.end),
12112 );
12113 start..end
12114 });
12115 s.select_ranges(new_ranges);
12116 });
12117 }
12118
12119 self.handle_input(text, cx);
12120 }
12121
12122 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12123 let Some(project) = self.project.as_ref() else {
12124 return false;
12125 };
12126 let project = project.read(cx);
12127
12128 let mut supports = false;
12129 self.buffer().read(cx).for_each_buffer(|buffer| {
12130 if !supports {
12131 supports = project
12132 .language_servers_for_buffer(buffer.read(cx), cx)
12133 .any(
12134 |(_, server)| match server.capabilities().inlay_hint_provider {
12135 Some(lsp::OneOf::Left(enabled)) => enabled,
12136 Some(lsp::OneOf::Right(_)) => true,
12137 None => false,
12138 },
12139 )
12140 }
12141 });
12142 supports
12143 }
12144
12145 pub fn focus(&self, cx: &mut WindowContext) {
12146 cx.focus(&self.focus_handle)
12147 }
12148
12149 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12150 self.focus_handle.is_focused(cx)
12151 }
12152
12153 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12154 cx.emit(EditorEvent::Focused);
12155
12156 if let Some(descendant) = self
12157 .last_focused_descendant
12158 .take()
12159 .and_then(|descendant| descendant.upgrade())
12160 {
12161 cx.focus(&descendant);
12162 } else {
12163 if let Some(blame) = self.blame.as_ref() {
12164 blame.update(cx, GitBlame::focus)
12165 }
12166
12167 self.blink_manager.update(cx, BlinkManager::enable);
12168 self.show_cursor_names(cx);
12169 self.buffer.update(cx, |buffer, cx| {
12170 buffer.finalize_last_transaction(cx);
12171 if self.leader_peer_id.is_none() {
12172 buffer.set_active_selections(
12173 &self.selections.disjoint_anchors(),
12174 self.selections.line_mode,
12175 self.cursor_shape,
12176 cx,
12177 );
12178 }
12179 });
12180 }
12181 }
12182
12183 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12184 cx.emit(EditorEvent::FocusedIn)
12185 }
12186
12187 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12188 if event.blurred != self.focus_handle {
12189 self.last_focused_descendant = Some(event.blurred);
12190 }
12191 }
12192
12193 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12194 self.blink_manager.update(cx, BlinkManager::disable);
12195 self.buffer
12196 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12197
12198 if let Some(blame) = self.blame.as_ref() {
12199 blame.update(cx, GitBlame::blur)
12200 }
12201 if !self.hover_state.focused(cx) {
12202 hide_hover(self, cx);
12203 }
12204
12205 self.hide_context_menu(cx);
12206 cx.emit(EditorEvent::Blurred);
12207 cx.notify();
12208 }
12209
12210 pub fn register_action<A: Action>(
12211 &mut self,
12212 listener: impl Fn(&A, &mut WindowContext) + 'static,
12213 ) -> Subscription {
12214 let id = self.next_editor_action_id.post_inc();
12215 let listener = Arc::new(listener);
12216 self.editor_actions.borrow_mut().insert(
12217 id,
12218 Box::new(move |cx| {
12219 let cx = cx.window_context();
12220 let listener = listener.clone();
12221 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
12222 let action = action.downcast_ref().unwrap();
12223 if phase == DispatchPhase::Bubble {
12224 listener(action, cx)
12225 }
12226 })
12227 }),
12228 );
12229
12230 let editor_actions = self.editor_actions.clone();
12231 Subscription::new(move || {
12232 editor_actions.borrow_mut().remove(&id);
12233 })
12234 }
12235
12236 pub fn file_header_size(&self) -> u32 {
12237 self.file_header_size
12238 }
12239
12240 pub fn revert(
12241 &mut self,
12242 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12243 cx: &mut ViewContext<Self>,
12244 ) {
12245 self.buffer().update(cx, |multi_buffer, cx| {
12246 for (buffer_id, changes) in revert_changes {
12247 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
12248 buffer.update(cx, |buffer, cx| {
12249 buffer.edit(
12250 changes.into_iter().map(|(range, text)| {
12251 (range, text.to_string().map(Arc::<str>::from))
12252 }),
12253 None,
12254 cx,
12255 );
12256 });
12257 }
12258 }
12259 });
12260 self.change_selections(None, cx, |selections| selections.refresh());
12261 }
12262
12263 pub fn to_pixel_point(
12264 &mut self,
12265 source: multi_buffer::Anchor,
12266 editor_snapshot: &EditorSnapshot,
12267 cx: &mut ViewContext<Self>,
12268 ) -> Option<gpui::Point<Pixels>> {
12269 let source_point = source.to_display_point(editor_snapshot);
12270 self.display_to_pixel_point(source_point, editor_snapshot, cx)
12271 }
12272
12273 pub fn display_to_pixel_point(
12274 &mut self,
12275 source: DisplayPoint,
12276 editor_snapshot: &EditorSnapshot,
12277 cx: &mut ViewContext<Self>,
12278 ) -> Option<gpui::Point<Pixels>> {
12279 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
12280 let text_layout_details = self.text_layout_details(cx);
12281 let scroll_top = text_layout_details
12282 .scroll_anchor
12283 .scroll_position(editor_snapshot)
12284 .y;
12285
12286 if source.row().as_f32() < scroll_top.floor() {
12287 return None;
12288 }
12289 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
12290 let source_y = line_height * (source.row().as_f32() - scroll_top);
12291 Some(gpui::Point::new(source_x, source_y))
12292 }
12293
12294 fn gutter_bounds(&self) -> Option<Bounds<Pixels>> {
12295 let bounds = self.last_bounds?;
12296 Some(element::gutter_bounds(bounds, self.gutter_dimensions))
12297 }
12298
12299 pub fn has_active_completions_menu(&self) -> bool {
12300 self.context_menu.read().as_ref().map_or(false, |menu| {
12301 menu.visible() && matches!(menu, ContextMenu::Completions(_))
12302 })
12303 }
12304
12305 pub fn register_addon<T: Addon>(&mut self, instance: T) {
12306 self.addons
12307 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
12308 }
12309
12310 pub fn unregister_addon<T: Addon>(&mut self) {
12311 self.addons.remove(&std::any::TypeId::of::<T>());
12312 }
12313
12314 pub fn addon<T: Addon>(&self) -> Option<&T> {
12315 let type_id = std::any::TypeId::of::<T>();
12316 self.addons
12317 .get(&type_id)
12318 .and_then(|item| item.to_any().downcast_ref::<T>())
12319 }
12320}
12321
12322fn hunks_for_selections(
12323 multi_buffer_snapshot: &MultiBufferSnapshot,
12324 selections: &[Selection<Anchor>],
12325) -> Vec<DiffHunk<MultiBufferRow>> {
12326 let buffer_rows_for_selections = selections.iter().map(|selection| {
12327 let head = selection.head();
12328 let tail = selection.tail();
12329 let start = MultiBufferRow(tail.to_point(multi_buffer_snapshot).row);
12330 let end = MultiBufferRow(head.to_point(multi_buffer_snapshot).row);
12331 if start > end {
12332 end..start
12333 } else {
12334 start..end
12335 }
12336 });
12337
12338 hunks_for_rows(buffer_rows_for_selections, multi_buffer_snapshot)
12339}
12340
12341pub fn hunks_for_rows(
12342 rows: impl Iterator<Item = Range<MultiBufferRow>>,
12343 multi_buffer_snapshot: &MultiBufferSnapshot,
12344) -> Vec<DiffHunk<MultiBufferRow>> {
12345 let mut hunks = Vec::new();
12346 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
12347 HashMap::default();
12348 for selected_multi_buffer_rows in rows {
12349 let query_rows =
12350 selected_multi_buffer_rows.start..selected_multi_buffer_rows.end.next_row();
12351 for hunk in multi_buffer_snapshot.git_diff_hunks_in_range(query_rows.clone()) {
12352 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
12353 // when the caret is just above or just below the deleted hunk.
12354 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
12355 let related_to_selection = if allow_adjacent {
12356 hunk.associated_range.overlaps(&query_rows)
12357 || hunk.associated_range.start == query_rows.end
12358 || hunk.associated_range.end == query_rows.start
12359 } else {
12360 // `selected_multi_buffer_rows` are inclusive (e.g. [2..2] means 2nd row is selected)
12361 // `hunk.associated_range` is exclusive (e.g. [2..3] means 2nd row is selected)
12362 hunk.associated_range.overlaps(&selected_multi_buffer_rows)
12363 || selected_multi_buffer_rows.end == hunk.associated_range.start
12364 };
12365 if related_to_selection {
12366 if !processed_buffer_rows
12367 .entry(hunk.buffer_id)
12368 .or_default()
12369 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
12370 {
12371 continue;
12372 }
12373 hunks.push(hunk);
12374 }
12375 }
12376 }
12377
12378 hunks
12379}
12380
12381pub trait CollaborationHub {
12382 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
12383 fn user_participant_indices<'a>(
12384 &self,
12385 cx: &'a AppContext,
12386 ) -> &'a HashMap<u64, ParticipantIndex>;
12387 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
12388}
12389
12390impl CollaborationHub for Model<Project> {
12391 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
12392 self.read(cx).collaborators()
12393 }
12394
12395 fn user_participant_indices<'a>(
12396 &self,
12397 cx: &'a AppContext,
12398 ) -> &'a HashMap<u64, ParticipantIndex> {
12399 self.read(cx).user_store().read(cx).participant_indices()
12400 }
12401
12402 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
12403 let this = self.read(cx);
12404 let user_ids = this.collaborators().values().map(|c| c.user_id);
12405 this.user_store().read_with(cx, |user_store, cx| {
12406 user_store.participant_names(user_ids, cx)
12407 })
12408 }
12409}
12410
12411pub trait CompletionProvider {
12412 fn completions(
12413 &self,
12414 buffer: &Model<Buffer>,
12415 buffer_position: text::Anchor,
12416 trigger: CompletionContext,
12417 cx: &mut ViewContext<Editor>,
12418 ) -> Task<Result<Vec<Completion>>>;
12419
12420 fn resolve_completions(
12421 &self,
12422 buffer: Model<Buffer>,
12423 completion_indices: Vec<usize>,
12424 completions: Arc<RwLock<Box<[Completion]>>>,
12425 cx: &mut ViewContext<Editor>,
12426 ) -> Task<Result<bool>>;
12427
12428 fn apply_additional_edits_for_completion(
12429 &self,
12430 buffer: Model<Buffer>,
12431 completion: Completion,
12432 push_to_history: bool,
12433 cx: &mut ViewContext<Editor>,
12434 ) -> Task<Result<Option<language::Transaction>>>;
12435
12436 fn is_completion_trigger(
12437 &self,
12438 buffer: &Model<Buffer>,
12439 position: language::Anchor,
12440 text: &str,
12441 trigger_in_words: bool,
12442 cx: &mut ViewContext<Editor>,
12443 ) -> bool;
12444
12445 fn sort_completions(&self) -> bool {
12446 true
12447 }
12448}
12449
12450fn snippet_completions(
12451 project: &Project,
12452 buffer: &Model<Buffer>,
12453 buffer_position: text::Anchor,
12454 cx: &mut AppContext,
12455) -> Vec<Completion> {
12456 let language = buffer.read(cx).language_at(buffer_position);
12457 let language_name = language.as_ref().map(|language| language.lsp_id());
12458 let snippet_store = project.snippets().read(cx);
12459 let snippets = snippet_store.snippets_for(language_name, cx);
12460
12461 if snippets.is_empty() {
12462 return vec![];
12463 }
12464 let snapshot = buffer.read(cx).text_snapshot();
12465 let chunks = snapshot.reversed_chunks_in_range(text::Anchor::MIN..buffer_position);
12466
12467 let mut lines = chunks.lines();
12468 let Some(line_at) = lines.next().filter(|line| !line.is_empty()) else {
12469 return vec![];
12470 };
12471
12472 let scope = language.map(|language| language.default_scope());
12473 let classifier = CharClassifier::new(scope).for_completion(true);
12474 let mut last_word = line_at
12475 .chars()
12476 .rev()
12477 .take_while(|c| classifier.is_word(*c))
12478 .collect::<String>();
12479 last_word = last_word.chars().rev().collect();
12480 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
12481 let to_lsp = |point: &text::Anchor| {
12482 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
12483 point_to_lsp(end)
12484 };
12485 let lsp_end = to_lsp(&buffer_position);
12486 snippets
12487 .into_iter()
12488 .filter_map(|snippet| {
12489 let matching_prefix = snippet
12490 .prefix
12491 .iter()
12492 .find(|prefix| prefix.starts_with(&last_word))?;
12493 let start = as_offset - last_word.len();
12494 let start = snapshot.anchor_before(start);
12495 let range = start..buffer_position;
12496 let lsp_start = to_lsp(&start);
12497 let lsp_range = lsp::Range {
12498 start: lsp_start,
12499 end: lsp_end,
12500 };
12501 Some(Completion {
12502 old_range: range,
12503 new_text: snippet.body.clone(),
12504 label: CodeLabel {
12505 text: matching_prefix.clone(),
12506 runs: vec![],
12507 filter_range: 0..matching_prefix.len(),
12508 },
12509 server_id: LanguageServerId(usize::MAX),
12510 documentation: snippet.description.clone().map(Documentation::SingleLine),
12511 lsp_completion: lsp::CompletionItem {
12512 label: snippet.prefix.first().unwrap().clone(),
12513 kind: Some(CompletionItemKind::SNIPPET),
12514 label_details: snippet.description.as_ref().map(|description| {
12515 lsp::CompletionItemLabelDetails {
12516 detail: Some(description.clone()),
12517 description: None,
12518 }
12519 }),
12520 insert_text_format: Some(InsertTextFormat::SNIPPET),
12521 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
12522 lsp::InsertReplaceEdit {
12523 new_text: snippet.body.clone(),
12524 insert: lsp_range,
12525 replace: lsp_range,
12526 },
12527 )),
12528 filter_text: Some(snippet.body.clone()),
12529 sort_text: Some(char::MAX.to_string()),
12530 ..Default::default()
12531 },
12532 confirm: None,
12533 })
12534 })
12535 .collect()
12536}
12537
12538impl CompletionProvider for Model<Project> {
12539 fn completions(
12540 &self,
12541 buffer: &Model<Buffer>,
12542 buffer_position: text::Anchor,
12543 options: CompletionContext,
12544 cx: &mut ViewContext<Editor>,
12545 ) -> Task<Result<Vec<Completion>>> {
12546 self.update(cx, |project, cx| {
12547 let snippets = snippet_completions(project, buffer, buffer_position, cx);
12548 let project_completions = project.completions(buffer, buffer_position, options, cx);
12549 cx.background_executor().spawn(async move {
12550 let mut completions = project_completions.await?;
12551 //let snippets = snippets.into_iter().;
12552 completions.extend(snippets);
12553 Ok(completions)
12554 })
12555 })
12556 }
12557
12558 fn resolve_completions(
12559 &self,
12560 buffer: Model<Buffer>,
12561 completion_indices: Vec<usize>,
12562 completions: Arc<RwLock<Box<[Completion]>>>,
12563 cx: &mut ViewContext<Editor>,
12564 ) -> Task<Result<bool>> {
12565 self.update(cx, |project, cx| {
12566 project.resolve_completions(buffer, completion_indices, completions, cx)
12567 })
12568 }
12569
12570 fn apply_additional_edits_for_completion(
12571 &self,
12572 buffer: Model<Buffer>,
12573 completion: Completion,
12574 push_to_history: bool,
12575 cx: &mut ViewContext<Editor>,
12576 ) -> Task<Result<Option<language::Transaction>>> {
12577 self.update(cx, |project, cx| {
12578 project.apply_additional_edits_for_completion(buffer, completion, push_to_history, cx)
12579 })
12580 }
12581
12582 fn is_completion_trigger(
12583 &self,
12584 buffer: &Model<Buffer>,
12585 position: language::Anchor,
12586 text: &str,
12587 trigger_in_words: bool,
12588 cx: &mut ViewContext<Editor>,
12589 ) -> bool {
12590 if !EditorSettings::get_global(cx).show_completions_on_input {
12591 return false;
12592 }
12593
12594 let mut chars = text.chars();
12595 let char = if let Some(char) = chars.next() {
12596 char
12597 } else {
12598 return false;
12599 };
12600 if chars.next().is_some() {
12601 return false;
12602 }
12603
12604 let buffer = buffer.read(cx);
12605 let classifier = buffer
12606 .snapshot()
12607 .char_classifier_at(position)
12608 .for_completion(true);
12609 if trigger_in_words && classifier.is_word(char) {
12610 return true;
12611 }
12612
12613 buffer
12614 .completion_triggers()
12615 .iter()
12616 .any(|string| string == text)
12617 }
12618}
12619
12620fn inlay_hint_settings(
12621 location: Anchor,
12622 snapshot: &MultiBufferSnapshot,
12623 cx: &mut ViewContext<'_, Editor>,
12624) -> InlayHintSettings {
12625 let file = snapshot.file_at(location);
12626 let language = snapshot.language_at(location);
12627 let settings = all_language_settings(file, cx);
12628 settings
12629 .language(language.map(|l| l.name()).as_ref())
12630 .inlay_hints
12631}
12632
12633fn consume_contiguous_rows(
12634 contiguous_row_selections: &mut Vec<Selection<Point>>,
12635 selection: &Selection<Point>,
12636 display_map: &DisplaySnapshot,
12637 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
12638) -> (MultiBufferRow, MultiBufferRow) {
12639 contiguous_row_selections.push(selection.clone());
12640 let start_row = MultiBufferRow(selection.start.row);
12641 let mut end_row = ending_row(selection, display_map);
12642
12643 while let Some(next_selection) = selections.peek() {
12644 if next_selection.start.row <= end_row.0 {
12645 end_row = ending_row(next_selection, display_map);
12646 contiguous_row_selections.push(selections.next().unwrap().clone());
12647 } else {
12648 break;
12649 }
12650 }
12651 (start_row, end_row)
12652}
12653
12654fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
12655 if next_selection.end.column > 0 || next_selection.is_empty() {
12656 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
12657 } else {
12658 MultiBufferRow(next_selection.end.row)
12659 }
12660}
12661
12662impl EditorSnapshot {
12663 pub fn remote_selections_in_range<'a>(
12664 &'a self,
12665 range: &'a Range<Anchor>,
12666 collaboration_hub: &dyn CollaborationHub,
12667 cx: &'a AppContext,
12668 ) -> impl 'a + Iterator<Item = RemoteSelection> {
12669 let participant_names = collaboration_hub.user_names(cx);
12670 let participant_indices = collaboration_hub.user_participant_indices(cx);
12671 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
12672 let collaborators_by_replica_id = collaborators_by_peer_id
12673 .iter()
12674 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
12675 .collect::<HashMap<_, _>>();
12676 self.buffer_snapshot
12677 .selections_in_range(range, false)
12678 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
12679 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
12680 let participant_index = participant_indices.get(&collaborator.user_id).copied();
12681 let user_name = participant_names.get(&collaborator.user_id).cloned();
12682 Some(RemoteSelection {
12683 replica_id,
12684 selection,
12685 cursor_shape,
12686 line_mode,
12687 participant_index,
12688 peer_id: collaborator.peer_id,
12689 user_name,
12690 })
12691 })
12692 }
12693
12694 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
12695 self.display_snapshot.buffer_snapshot.language_at(position)
12696 }
12697
12698 pub fn is_focused(&self) -> bool {
12699 self.is_focused
12700 }
12701
12702 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
12703 self.placeholder_text.as_ref()
12704 }
12705
12706 pub fn scroll_position(&self) -> gpui::Point<f32> {
12707 self.scroll_anchor.scroll_position(&self.display_snapshot)
12708 }
12709
12710 fn gutter_dimensions(
12711 &self,
12712 font_id: FontId,
12713 font_size: Pixels,
12714 em_width: Pixels,
12715 max_line_number_width: Pixels,
12716 cx: &AppContext,
12717 ) -> GutterDimensions {
12718 if !self.show_gutter {
12719 return GutterDimensions::default();
12720 }
12721 let descent = cx.text_system().descent(font_id, font_size);
12722
12723 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
12724 matches!(
12725 ProjectSettings::get_global(cx).git.git_gutter,
12726 Some(GitGutterSetting::TrackedFiles)
12727 )
12728 });
12729 let gutter_settings = EditorSettings::get_global(cx).gutter;
12730 let show_line_numbers = self
12731 .show_line_numbers
12732 .unwrap_or(gutter_settings.line_numbers);
12733 let line_gutter_width = if show_line_numbers {
12734 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
12735 let min_width_for_number_on_gutter = em_width * 4.0;
12736 max_line_number_width.max(min_width_for_number_on_gutter)
12737 } else {
12738 0.0.into()
12739 };
12740
12741 let show_code_actions = self
12742 .show_code_actions
12743 .unwrap_or(gutter_settings.code_actions);
12744
12745 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
12746
12747 let git_blame_entries_width = self
12748 .render_git_blame_gutter
12749 .then_some(em_width * GIT_BLAME_GUTTER_WIDTH_CHARS);
12750
12751 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
12752 left_padding += if show_code_actions || show_runnables {
12753 em_width * 3.0
12754 } else if show_git_gutter && show_line_numbers {
12755 em_width * 2.0
12756 } else if show_git_gutter || show_line_numbers {
12757 em_width
12758 } else {
12759 px(0.)
12760 };
12761
12762 let right_padding = if gutter_settings.folds && show_line_numbers {
12763 em_width * 4.0
12764 } else if gutter_settings.folds {
12765 em_width * 3.0
12766 } else if show_line_numbers {
12767 em_width
12768 } else {
12769 px(0.)
12770 };
12771
12772 GutterDimensions {
12773 left_padding,
12774 right_padding,
12775 width: line_gutter_width + left_padding + right_padding,
12776 margin: -descent,
12777 git_blame_entries_width,
12778 }
12779 }
12780
12781 pub fn render_fold_toggle(
12782 &self,
12783 buffer_row: MultiBufferRow,
12784 row_contains_cursor: bool,
12785 editor: View<Editor>,
12786 cx: &mut WindowContext,
12787 ) -> Option<AnyElement> {
12788 let folded = self.is_line_folded(buffer_row);
12789
12790 if let Some(crease) = self
12791 .crease_snapshot
12792 .query_row(buffer_row, &self.buffer_snapshot)
12793 {
12794 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
12795 if folded {
12796 editor.update(cx, |editor, cx| {
12797 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
12798 });
12799 } else {
12800 editor.update(cx, |editor, cx| {
12801 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
12802 });
12803 }
12804 });
12805
12806 Some((crease.render_toggle)(
12807 buffer_row,
12808 folded,
12809 toggle_callback,
12810 cx,
12811 ))
12812 } else if folded
12813 || (self.starts_indent(buffer_row) && (row_contains_cursor || self.gutter_hovered))
12814 {
12815 Some(
12816 Disclosure::new(("indent-fold-indicator", buffer_row.0), !folded)
12817 .selected(folded)
12818 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
12819 if folded {
12820 this.unfold_at(&UnfoldAt { buffer_row }, cx);
12821 } else {
12822 this.fold_at(&FoldAt { buffer_row }, cx);
12823 }
12824 }))
12825 .into_any_element(),
12826 )
12827 } else {
12828 None
12829 }
12830 }
12831
12832 pub fn render_crease_trailer(
12833 &self,
12834 buffer_row: MultiBufferRow,
12835 cx: &mut WindowContext,
12836 ) -> Option<AnyElement> {
12837 let folded = self.is_line_folded(buffer_row);
12838 let crease = self
12839 .crease_snapshot
12840 .query_row(buffer_row, &self.buffer_snapshot)?;
12841 Some((crease.render_trailer)(buffer_row, folded, cx))
12842 }
12843}
12844
12845impl Deref for EditorSnapshot {
12846 type Target = DisplaySnapshot;
12847
12848 fn deref(&self) -> &Self::Target {
12849 &self.display_snapshot
12850 }
12851}
12852
12853#[derive(Clone, Debug, PartialEq, Eq)]
12854pub enum EditorEvent {
12855 InputIgnored {
12856 text: Arc<str>,
12857 },
12858 InputHandled {
12859 utf16_range_to_replace: Option<Range<isize>>,
12860 text: Arc<str>,
12861 },
12862 ExcerptsAdded {
12863 buffer: Model<Buffer>,
12864 predecessor: ExcerptId,
12865 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
12866 },
12867 ExcerptsRemoved {
12868 ids: Vec<ExcerptId>,
12869 },
12870 ExcerptsEdited {
12871 ids: Vec<ExcerptId>,
12872 },
12873 ExcerptsExpanded {
12874 ids: Vec<ExcerptId>,
12875 },
12876 BufferEdited,
12877 Edited {
12878 transaction_id: clock::Lamport,
12879 },
12880 Reparsed(BufferId),
12881 Focused,
12882 FocusedIn,
12883 Blurred,
12884 DirtyChanged,
12885 Saved,
12886 TitleChanged,
12887 DiffBaseChanged,
12888 SelectionsChanged {
12889 local: bool,
12890 },
12891 ScrollPositionChanged {
12892 local: bool,
12893 autoscroll: bool,
12894 },
12895 Closed,
12896 TransactionUndone {
12897 transaction_id: clock::Lamport,
12898 },
12899 TransactionBegun {
12900 transaction_id: clock::Lamport,
12901 },
12902}
12903
12904impl EventEmitter<EditorEvent> for Editor {}
12905
12906impl FocusableView for Editor {
12907 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
12908 self.focus_handle.clone()
12909 }
12910}
12911
12912impl Render for Editor {
12913 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
12914 let settings = ThemeSettings::get_global(cx);
12915
12916 let text_style = match self.mode {
12917 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
12918 color: cx.theme().colors().editor_foreground,
12919 font_family: settings.ui_font.family.clone(),
12920 font_features: settings.ui_font.features.clone(),
12921 font_fallbacks: settings.ui_font.fallbacks.clone(),
12922 font_size: rems(0.875).into(),
12923 font_weight: settings.ui_font.weight,
12924 line_height: relative(settings.buffer_line_height.value()),
12925 ..Default::default()
12926 },
12927 EditorMode::Full => TextStyle {
12928 color: cx.theme().colors().editor_foreground,
12929 font_family: settings.buffer_font.family.clone(),
12930 font_features: settings.buffer_font.features.clone(),
12931 font_fallbacks: settings.buffer_font.fallbacks.clone(),
12932 font_size: settings.buffer_font_size(cx).into(),
12933 font_weight: settings.buffer_font.weight,
12934 line_height: relative(settings.buffer_line_height.value()),
12935 ..Default::default()
12936 },
12937 };
12938
12939 let background = match self.mode {
12940 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
12941 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
12942 EditorMode::Full => cx.theme().colors().editor_background,
12943 };
12944
12945 EditorElement::new(
12946 cx.view(),
12947 EditorStyle {
12948 background,
12949 local_player: cx.theme().players().local(),
12950 text: text_style,
12951 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
12952 syntax: cx.theme().syntax().clone(),
12953 status: cx.theme().status().clone(),
12954 inlay_hints_style: HighlightStyle {
12955 color: Some(cx.theme().status().hint),
12956 ..HighlightStyle::default()
12957 },
12958 suggestions_style: HighlightStyle {
12959 color: Some(cx.theme().status().predictive),
12960 ..HighlightStyle::default()
12961 },
12962 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
12963 },
12964 )
12965 }
12966}
12967
12968impl ViewInputHandler for Editor {
12969 fn text_for_range(
12970 &mut self,
12971 range_utf16: Range<usize>,
12972 cx: &mut ViewContext<Self>,
12973 ) -> Option<String> {
12974 Some(
12975 self.buffer
12976 .read(cx)
12977 .read(cx)
12978 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
12979 .collect(),
12980 )
12981 }
12982
12983 fn selected_text_range(
12984 &mut self,
12985 ignore_disabled_input: bool,
12986 cx: &mut ViewContext<Self>,
12987 ) -> Option<UTF16Selection> {
12988 // Prevent the IME menu from appearing when holding down an alphabetic key
12989 // while input is disabled.
12990 if !ignore_disabled_input && !self.input_enabled {
12991 return None;
12992 }
12993
12994 let selection = self.selections.newest::<OffsetUtf16>(cx);
12995 let range = selection.range();
12996
12997 Some(UTF16Selection {
12998 range: range.start.0..range.end.0,
12999 reversed: selection.reversed,
13000 })
13001 }
13002
13003 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
13004 let snapshot = self.buffer.read(cx).read(cx);
13005 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
13006 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
13007 }
13008
13009 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
13010 self.clear_highlights::<InputComposition>(cx);
13011 self.ime_transaction.take();
13012 }
13013
13014 fn replace_text_in_range(
13015 &mut self,
13016 range_utf16: Option<Range<usize>>,
13017 text: &str,
13018 cx: &mut ViewContext<Self>,
13019 ) {
13020 if !self.input_enabled {
13021 cx.emit(EditorEvent::InputIgnored { text: text.into() });
13022 return;
13023 }
13024
13025 self.transact(cx, |this, cx| {
13026 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
13027 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
13028 Some(this.selection_replacement_ranges(range_utf16, cx))
13029 } else {
13030 this.marked_text_ranges(cx)
13031 };
13032
13033 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
13034 let newest_selection_id = this.selections.newest_anchor().id;
13035 this.selections
13036 .all::<OffsetUtf16>(cx)
13037 .iter()
13038 .zip(ranges_to_replace.iter())
13039 .find_map(|(selection, range)| {
13040 if selection.id == newest_selection_id {
13041 Some(
13042 (range.start.0 as isize - selection.head().0 as isize)
13043 ..(range.end.0 as isize - selection.head().0 as isize),
13044 )
13045 } else {
13046 None
13047 }
13048 })
13049 });
13050
13051 cx.emit(EditorEvent::InputHandled {
13052 utf16_range_to_replace: range_to_replace,
13053 text: text.into(),
13054 });
13055
13056 if let Some(new_selected_ranges) = new_selected_ranges {
13057 this.change_selections(None, cx, |selections| {
13058 selections.select_ranges(new_selected_ranges)
13059 });
13060 this.backspace(&Default::default(), cx);
13061 }
13062
13063 this.handle_input(text, cx);
13064 });
13065
13066 if let Some(transaction) = self.ime_transaction {
13067 self.buffer.update(cx, |buffer, cx| {
13068 buffer.group_until_transaction(transaction, cx);
13069 });
13070 }
13071
13072 self.unmark_text(cx);
13073 }
13074
13075 fn replace_and_mark_text_in_range(
13076 &mut self,
13077 range_utf16: Option<Range<usize>>,
13078 text: &str,
13079 new_selected_range_utf16: Option<Range<usize>>,
13080 cx: &mut ViewContext<Self>,
13081 ) {
13082 if !self.input_enabled {
13083 return;
13084 }
13085
13086 let transaction = self.transact(cx, |this, cx| {
13087 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
13088 let snapshot = this.buffer.read(cx).read(cx);
13089 if let Some(relative_range_utf16) = range_utf16.as_ref() {
13090 for marked_range in &mut marked_ranges {
13091 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
13092 marked_range.start.0 += relative_range_utf16.start;
13093 marked_range.start =
13094 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
13095 marked_range.end =
13096 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
13097 }
13098 }
13099 Some(marked_ranges)
13100 } else if let Some(range_utf16) = range_utf16 {
13101 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
13102 Some(this.selection_replacement_ranges(range_utf16, cx))
13103 } else {
13104 None
13105 };
13106
13107 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
13108 let newest_selection_id = this.selections.newest_anchor().id;
13109 this.selections
13110 .all::<OffsetUtf16>(cx)
13111 .iter()
13112 .zip(ranges_to_replace.iter())
13113 .find_map(|(selection, range)| {
13114 if selection.id == newest_selection_id {
13115 Some(
13116 (range.start.0 as isize - selection.head().0 as isize)
13117 ..(range.end.0 as isize - selection.head().0 as isize),
13118 )
13119 } else {
13120 None
13121 }
13122 })
13123 });
13124
13125 cx.emit(EditorEvent::InputHandled {
13126 utf16_range_to_replace: range_to_replace,
13127 text: text.into(),
13128 });
13129
13130 if let Some(ranges) = ranges_to_replace {
13131 this.change_selections(None, cx, |s| s.select_ranges(ranges));
13132 }
13133
13134 let marked_ranges = {
13135 let snapshot = this.buffer.read(cx).read(cx);
13136 this.selections
13137 .disjoint_anchors()
13138 .iter()
13139 .map(|selection| {
13140 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
13141 })
13142 .collect::<Vec<_>>()
13143 };
13144
13145 if text.is_empty() {
13146 this.unmark_text(cx);
13147 } else {
13148 this.highlight_text::<InputComposition>(
13149 marked_ranges.clone(),
13150 HighlightStyle {
13151 underline: Some(UnderlineStyle {
13152 thickness: px(1.),
13153 color: None,
13154 wavy: false,
13155 }),
13156 ..Default::default()
13157 },
13158 cx,
13159 );
13160 }
13161
13162 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
13163 let use_autoclose = this.use_autoclose;
13164 let use_auto_surround = this.use_auto_surround;
13165 this.set_use_autoclose(false);
13166 this.set_use_auto_surround(false);
13167 this.handle_input(text, cx);
13168 this.set_use_autoclose(use_autoclose);
13169 this.set_use_auto_surround(use_auto_surround);
13170
13171 if let Some(new_selected_range) = new_selected_range_utf16 {
13172 let snapshot = this.buffer.read(cx).read(cx);
13173 let new_selected_ranges = marked_ranges
13174 .into_iter()
13175 .map(|marked_range| {
13176 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
13177 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
13178 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
13179 snapshot.clip_offset_utf16(new_start, Bias::Left)
13180 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
13181 })
13182 .collect::<Vec<_>>();
13183
13184 drop(snapshot);
13185 this.change_selections(None, cx, |selections| {
13186 selections.select_ranges(new_selected_ranges)
13187 });
13188 }
13189 });
13190
13191 self.ime_transaction = self.ime_transaction.or(transaction);
13192 if let Some(transaction) = self.ime_transaction {
13193 self.buffer.update(cx, |buffer, cx| {
13194 buffer.group_until_transaction(transaction, cx);
13195 });
13196 }
13197
13198 if self.text_highlights::<InputComposition>(cx).is_none() {
13199 self.ime_transaction.take();
13200 }
13201 }
13202
13203 fn bounds_for_range(
13204 &mut self,
13205 range_utf16: Range<usize>,
13206 element_bounds: gpui::Bounds<Pixels>,
13207 cx: &mut ViewContext<Self>,
13208 ) -> Option<gpui::Bounds<Pixels>> {
13209 let text_layout_details = self.text_layout_details(cx);
13210 let style = &text_layout_details.editor_style;
13211 let font_id = cx.text_system().resolve_font(&style.text.font());
13212 let font_size = style.text.font_size.to_pixels(cx.rem_size());
13213 let line_height = style.text.line_height_in_pixels(cx.rem_size());
13214
13215 let em_width = cx
13216 .text_system()
13217 .typographic_bounds(font_id, font_size, 'm')
13218 .unwrap()
13219 .size
13220 .width;
13221
13222 let snapshot = self.snapshot(cx);
13223 let scroll_position = snapshot.scroll_position();
13224 let scroll_left = scroll_position.x * em_width;
13225
13226 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
13227 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
13228 + self.gutter_dimensions.width;
13229 let y = line_height * (start.row().as_f32() - scroll_position.y);
13230
13231 Some(Bounds {
13232 origin: element_bounds.origin + point(x, y),
13233 size: size(em_width, line_height),
13234 })
13235 }
13236}
13237
13238trait SelectionExt {
13239 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
13240 fn spanned_rows(
13241 &self,
13242 include_end_if_at_line_start: bool,
13243 map: &DisplaySnapshot,
13244 ) -> Range<MultiBufferRow>;
13245}
13246
13247impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
13248 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
13249 let start = self
13250 .start
13251 .to_point(&map.buffer_snapshot)
13252 .to_display_point(map);
13253 let end = self
13254 .end
13255 .to_point(&map.buffer_snapshot)
13256 .to_display_point(map);
13257 if self.reversed {
13258 end..start
13259 } else {
13260 start..end
13261 }
13262 }
13263
13264 fn spanned_rows(
13265 &self,
13266 include_end_if_at_line_start: bool,
13267 map: &DisplaySnapshot,
13268 ) -> Range<MultiBufferRow> {
13269 let start = self.start.to_point(&map.buffer_snapshot);
13270 let mut end = self.end.to_point(&map.buffer_snapshot);
13271 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
13272 end.row -= 1;
13273 }
13274
13275 let buffer_start = map.prev_line_boundary(start).0;
13276 let buffer_end = map.next_line_boundary(end).0;
13277 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
13278 }
13279}
13280
13281impl<T: InvalidationRegion> InvalidationStack<T> {
13282 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
13283 where
13284 S: Clone + ToOffset,
13285 {
13286 while let Some(region) = self.last() {
13287 let all_selections_inside_invalidation_ranges =
13288 if selections.len() == region.ranges().len() {
13289 selections
13290 .iter()
13291 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
13292 .all(|(selection, invalidation_range)| {
13293 let head = selection.head().to_offset(buffer);
13294 invalidation_range.start <= head && invalidation_range.end >= head
13295 })
13296 } else {
13297 false
13298 };
13299
13300 if all_selections_inside_invalidation_ranges {
13301 break;
13302 } else {
13303 self.pop();
13304 }
13305 }
13306 }
13307}
13308
13309impl<T> Default for InvalidationStack<T> {
13310 fn default() -> Self {
13311 Self(Default::default())
13312 }
13313}
13314
13315impl<T> Deref for InvalidationStack<T> {
13316 type Target = Vec<T>;
13317
13318 fn deref(&self) -> &Self::Target {
13319 &self.0
13320 }
13321}
13322
13323impl<T> DerefMut for InvalidationStack<T> {
13324 fn deref_mut(&mut self) -> &mut Self::Target {
13325 &mut self.0
13326 }
13327}
13328
13329impl InvalidationRegion for SnippetState {
13330 fn ranges(&self) -> &[Range<Anchor>] {
13331 &self.ranges[self.active_index]
13332 }
13333}
13334
13335pub fn diagnostic_block_renderer(
13336 diagnostic: Diagnostic,
13337 max_message_rows: Option<u8>,
13338 allow_closing: bool,
13339 _is_valid: bool,
13340) -> RenderBlock {
13341 let (text_without_backticks, code_ranges) =
13342 highlight_diagnostic_message(&diagnostic, max_message_rows);
13343
13344 Box::new(move |cx: &mut BlockContext| {
13345 let group_id: SharedString = cx.block_id.to_string().into();
13346
13347 let mut text_style = cx.text_style().clone();
13348 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
13349 let theme_settings = ThemeSettings::get_global(cx);
13350 text_style.font_family = theme_settings.buffer_font.family.clone();
13351 text_style.font_style = theme_settings.buffer_font.style;
13352 text_style.font_features = theme_settings.buffer_font.features.clone();
13353 text_style.font_weight = theme_settings.buffer_font.weight;
13354
13355 let multi_line_diagnostic = diagnostic.message.contains('\n');
13356
13357 let buttons = |diagnostic: &Diagnostic, block_id: BlockId| {
13358 if multi_line_diagnostic {
13359 v_flex()
13360 } else {
13361 h_flex()
13362 }
13363 .when(allow_closing, |div| {
13364 div.children(diagnostic.is_primary.then(|| {
13365 IconButton::new(("close-block", EntityId::from(block_id)), IconName::XCircle)
13366 .icon_color(Color::Muted)
13367 .size(ButtonSize::Compact)
13368 .style(ButtonStyle::Transparent)
13369 .visible_on_hover(group_id.clone())
13370 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
13371 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
13372 }))
13373 })
13374 .child(
13375 IconButton::new(("copy-block", EntityId::from(block_id)), IconName::Copy)
13376 .icon_color(Color::Muted)
13377 .size(ButtonSize::Compact)
13378 .style(ButtonStyle::Transparent)
13379 .visible_on_hover(group_id.clone())
13380 .on_click({
13381 let message = diagnostic.message.clone();
13382 move |_click, cx| {
13383 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
13384 }
13385 })
13386 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
13387 )
13388 };
13389
13390 let icon_size = buttons(&diagnostic, cx.block_id)
13391 .into_any_element()
13392 .layout_as_root(AvailableSpace::min_size(), cx);
13393
13394 h_flex()
13395 .id(cx.block_id)
13396 .group(group_id.clone())
13397 .relative()
13398 .size_full()
13399 .pl(cx.gutter_dimensions.width)
13400 .w(cx.max_width + cx.gutter_dimensions.width)
13401 .child(
13402 div()
13403 .flex()
13404 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
13405 .flex_shrink(),
13406 )
13407 .child(buttons(&diagnostic, cx.block_id))
13408 .child(div().flex().flex_shrink_0().child(
13409 StyledText::new(text_without_backticks.clone()).with_highlights(
13410 &text_style,
13411 code_ranges.iter().map(|range| {
13412 (
13413 range.clone(),
13414 HighlightStyle {
13415 font_weight: Some(FontWeight::BOLD),
13416 ..Default::default()
13417 },
13418 )
13419 }),
13420 ),
13421 ))
13422 .into_any_element()
13423 })
13424}
13425
13426pub fn highlight_diagnostic_message(
13427 diagnostic: &Diagnostic,
13428 mut max_message_rows: Option<u8>,
13429) -> (SharedString, Vec<Range<usize>>) {
13430 let mut text_without_backticks = String::new();
13431 let mut code_ranges = Vec::new();
13432
13433 if let Some(source) = &diagnostic.source {
13434 text_without_backticks.push_str(source);
13435 code_ranges.push(0..source.len());
13436 text_without_backticks.push_str(": ");
13437 }
13438
13439 let mut prev_offset = 0;
13440 let mut in_code_block = false;
13441 let has_row_limit = max_message_rows.is_some();
13442 let mut newline_indices = diagnostic
13443 .message
13444 .match_indices('\n')
13445 .filter(|_| has_row_limit)
13446 .map(|(ix, _)| ix)
13447 .fuse()
13448 .peekable();
13449
13450 for (quote_ix, _) in diagnostic
13451 .message
13452 .match_indices('`')
13453 .chain([(diagnostic.message.len(), "")])
13454 {
13455 let mut first_newline_ix = None;
13456 let mut last_newline_ix = None;
13457 while let Some(newline_ix) = newline_indices.peek() {
13458 if *newline_ix < quote_ix {
13459 if first_newline_ix.is_none() {
13460 first_newline_ix = Some(*newline_ix);
13461 }
13462 last_newline_ix = Some(*newline_ix);
13463
13464 if let Some(rows_left) = &mut max_message_rows {
13465 if *rows_left == 0 {
13466 break;
13467 } else {
13468 *rows_left -= 1;
13469 }
13470 }
13471 let _ = newline_indices.next();
13472 } else {
13473 break;
13474 }
13475 }
13476 let prev_len = text_without_backticks.len();
13477 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
13478 text_without_backticks.push_str(new_text);
13479 if in_code_block {
13480 code_ranges.push(prev_len..text_without_backticks.len());
13481 }
13482 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
13483 in_code_block = !in_code_block;
13484 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
13485 text_without_backticks.push_str("...");
13486 break;
13487 }
13488 }
13489
13490 (text_without_backticks.into(), code_ranges)
13491}
13492
13493fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
13494 match severity {
13495 DiagnosticSeverity::ERROR => colors.error,
13496 DiagnosticSeverity::WARNING => colors.warning,
13497 DiagnosticSeverity::INFORMATION => colors.info,
13498 DiagnosticSeverity::HINT => colors.info,
13499 _ => colors.ignored,
13500 }
13501}
13502
13503pub fn styled_runs_for_code_label<'a>(
13504 label: &'a CodeLabel,
13505 syntax_theme: &'a theme::SyntaxTheme,
13506) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
13507 let fade_out = HighlightStyle {
13508 fade_out: Some(0.35),
13509 ..Default::default()
13510 };
13511
13512 let mut prev_end = label.filter_range.end;
13513 label
13514 .runs
13515 .iter()
13516 .enumerate()
13517 .flat_map(move |(ix, (range, highlight_id))| {
13518 let style = if let Some(style) = highlight_id.style(syntax_theme) {
13519 style
13520 } else {
13521 return Default::default();
13522 };
13523 let mut muted_style = style;
13524 muted_style.highlight(fade_out);
13525
13526 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
13527 if range.start >= label.filter_range.end {
13528 if range.start > prev_end {
13529 runs.push((prev_end..range.start, fade_out));
13530 }
13531 runs.push((range.clone(), muted_style));
13532 } else if range.end <= label.filter_range.end {
13533 runs.push((range.clone(), style));
13534 } else {
13535 runs.push((range.start..label.filter_range.end, style));
13536 runs.push((label.filter_range.end..range.end, muted_style));
13537 }
13538 prev_end = cmp::max(prev_end, range.end);
13539
13540 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
13541 runs.push((prev_end..label.text.len(), fade_out));
13542 }
13543
13544 runs
13545 })
13546}
13547
13548pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
13549 let mut prev_index = 0;
13550 let mut prev_codepoint: Option<char> = None;
13551 text.char_indices()
13552 .chain([(text.len(), '\0')])
13553 .filter_map(move |(index, codepoint)| {
13554 let prev_codepoint = prev_codepoint.replace(codepoint)?;
13555 let is_boundary = index == text.len()
13556 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
13557 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
13558 if is_boundary {
13559 let chunk = &text[prev_index..index];
13560 prev_index = index;
13561 Some(chunk)
13562 } else {
13563 None
13564 }
13565 })
13566}
13567
13568pub trait RangeToAnchorExt: Sized {
13569 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
13570
13571 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
13572 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
13573 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
13574 }
13575}
13576
13577impl<T: ToOffset> RangeToAnchorExt for Range<T> {
13578 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
13579 let start_offset = self.start.to_offset(snapshot);
13580 let end_offset = self.end.to_offset(snapshot);
13581 if start_offset == end_offset {
13582 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
13583 } else {
13584 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
13585 }
13586 }
13587}
13588
13589pub trait RowExt {
13590 fn as_f32(&self) -> f32;
13591
13592 fn next_row(&self) -> Self;
13593
13594 fn previous_row(&self) -> Self;
13595
13596 fn minus(&self, other: Self) -> u32;
13597}
13598
13599impl RowExt for DisplayRow {
13600 fn as_f32(&self) -> f32 {
13601 self.0 as f32
13602 }
13603
13604 fn next_row(&self) -> Self {
13605 Self(self.0 + 1)
13606 }
13607
13608 fn previous_row(&self) -> Self {
13609 Self(self.0.saturating_sub(1))
13610 }
13611
13612 fn minus(&self, other: Self) -> u32 {
13613 self.0 - other.0
13614 }
13615}
13616
13617impl RowExt for MultiBufferRow {
13618 fn as_f32(&self) -> f32 {
13619 self.0 as f32
13620 }
13621
13622 fn next_row(&self) -> Self {
13623 Self(self.0 + 1)
13624 }
13625
13626 fn previous_row(&self) -> Self {
13627 Self(self.0.saturating_sub(1))
13628 }
13629
13630 fn minus(&self, other: Self) -> u32 {
13631 self.0 - other.0
13632 }
13633}
13634
13635trait RowRangeExt {
13636 type Row;
13637
13638 fn len(&self) -> usize;
13639
13640 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
13641}
13642
13643impl RowRangeExt for Range<MultiBufferRow> {
13644 type Row = MultiBufferRow;
13645
13646 fn len(&self) -> usize {
13647 (self.end.0 - self.start.0) as usize
13648 }
13649
13650 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
13651 (self.start.0..self.end.0).map(MultiBufferRow)
13652 }
13653}
13654
13655impl RowRangeExt for Range<DisplayRow> {
13656 type Row = DisplayRow;
13657
13658 fn len(&self) -> usize {
13659 (self.end.0 - self.start.0) as usize
13660 }
13661
13662 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
13663 (self.start.0..self.end.0).map(DisplayRow)
13664 }
13665}
13666
13667fn hunk_status(hunk: &DiffHunk<MultiBufferRow>) -> DiffHunkStatus {
13668 if hunk.diff_base_byte_range.is_empty() {
13669 DiffHunkStatus::Added
13670 } else if hunk.associated_range.is_empty() {
13671 DiffHunkStatus::Removed
13672 } else {
13673 DiffHunkStatus::Modified
13674 }
13675}
13676
13677/// If select range has more than one line, we
13678/// just point the cursor to range.start.
13679fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
13680 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
13681 range
13682 } else {
13683 range.start..range.start
13684 }
13685}