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 code_context_menus;
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 indent_guides;
29mod inlay_hint_cache;
30pub mod items;
31mod linked_editing_ranges;
32mod lsp_ext;
33mod mouse_context_menu;
34pub mod movement;
35mod persistence;
36mod proposed_changes_editor;
37mod rust_analyzer_ext;
38pub mod scroll;
39mod selections_collection;
40pub mod tasks;
41
42#[cfg(test)]
43mod editor_tests;
44#[cfg(test)]
45mod inline_completion_tests;
46mod signature_help;
47#[cfg(any(test, feature = "test-support"))]
48pub mod test;
49
50pub(crate) use actions::*;
51pub use actions::{OpenExcerpts, OpenExcerptsSplit};
52use aho_corasick::AhoCorasick;
53use anyhow::{anyhow, Context as _, Result};
54use blink_manager::BlinkManager;
55use client::{Collaborator, ParticipantIndex};
56use clock::ReplicaId;
57use collections::{BTreeMap, HashMap, HashSet, VecDeque};
58use convert_case::{Case, Casing};
59use display_map::*;
60pub use display_map::{DisplayPoint, FoldPlaceholder};
61pub use editor_settings::{
62 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
63};
64pub use editor_settings_controls::*;
65use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
66pub use element::{
67 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
68};
69use futures::{future, FutureExt};
70use fuzzy::StringMatchCandidate;
71
72use code_context_menus::{
73 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
74 CompletionsMenu, ContextMenuOrigin,
75};
76use diff::DiffHunkStatus;
77use git::blame::GitBlame;
78use gpui::{
79 div, impl_actions, linear_color_stop, linear_gradient, point, prelude::*, pulsating_between,
80 px, relative, size, Action, Animation, AnimationExt, AnyElement, App, AsyncWindowContext,
81 AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context, DispatchPhase, ElementId,
82 Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId,
83 FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Modifiers, MouseButton,
84 MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled,
85 StyledText, Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection,
86 UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
87};
88use highlight_matching_bracket::refresh_matching_bracket_highlights;
89use hover_popover::{hide_hover, HoverState};
90use indent_guides::ActiveIndentGuidesState;
91use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
92pub use inline_completion::Direction;
93use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
94pub use items::MAX_TAB_TITLE_LEN;
95use itertools::Itertools;
96use language::{
97 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
98 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
99 CompletionDocumentation, CursorShape, Diagnostic, EditPreview, HighlightedText, IndentKind,
100 IndentSize, InlineCompletionPreviewMode, Language, OffsetRangeExt, Point, Selection,
101 SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
102};
103use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
104use linked_editing_ranges::refresh_linked_ranges;
105use mouse_context_menu::MouseContextMenu;
106pub use proposed_changes_editor::{
107 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
108};
109use similar::{ChangeTag, TextDiff};
110use std::iter::Peekable;
111use task::{ResolvedTask, TaskTemplate, TaskVariables};
112
113use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
114pub use lsp::CompletionContext;
115use lsp::{
116 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
117 LanguageServerId, LanguageServerName,
118};
119
120use language::BufferSnapshot;
121use movement::TextLayoutDetails;
122pub use multi_buffer::{
123 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
124 ToOffset, ToPoint,
125};
126use multi_buffer::{
127 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
128 ToOffsetUtf16,
129};
130use project::{
131 lsp_store::{FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
132 project_settings::{GitGutterSetting, ProjectSettings},
133 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
134 LspStore, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
135};
136use rand::prelude::*;
137use rpc::{proto::*, ErrorExt};
138use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
139use selections_collection::{
140 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
141};
142use serde::{Deserialize, Serialize};
143use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
144use smallvec::SmallVec;
145use snippet::Snippet;
146use std::{
147 any::TypeId,
148 borrow::Cow,
149 cell::RefCell,
150 cmp::{self, Ordering, Reverse},
151 mem,
152 num::NonZeroU32,
153 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
154 path::{Path, PathBuf},
155 rc::Rc,
156 sync::Arc,
157 time::{Duration, Instant},
158};
159pub use sum_tree::Bias;
160use sum_tree::TreeMap;
161use text::{BufferId, OffsetUtf16, Rope};
162use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
163use ui::{
164 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
165 Tooltip,
166};
167use util::{defer, maybe, post_inc, RangeExt, ResultExt, TakeUntilExt, TryFutureExt};
168use workspace::item::{ItemHandle, PreviewTabsSettings};
169use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
170use workspace::{
171 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
172};
173use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
174
175use crate::hover_links::{find_url, find_url_from_range};
176use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
177
178pub const FILE_HEADER_HEIGHT: u32 = 2;
179pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
180pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
181pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
182const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
183const MAX_LINE_LEN: usize = 1024;
184const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
185const MAX_SELECTION_HISTORY_LEN: usize = 1024;
186pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
187#[doc(hidden)]
188pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
189
190pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
191pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
192
193pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
194pub(crate) const EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT: &str =
195 "edit_prediction_requires_modifier";
196
197pub fn render_parsed_markdown(
198 element_id: impl Into<ElementId>,
199 parsed: &language::ParsedMarkdown,
200 editor_style: &EditorStyle,
201 workspace: Option<WeakEntity<Workspace>>,
202 cx: &mut App,
203) -> InteractiveText {
204 let code_span_background_color = cx
205 .theme()
206 .colors()
207 .editor_document_highlight_read_background;
208
209 let highlights = gpui::combine_highlights(
210 parsed.highlights.iter().filter_map(|(range, highlight)| {
211 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
212 Some((range.clone(), highlight))
213 }),
214 parsed
215 .regions
216 .iter()
217 .zip(&parsed.region_ranges)
218 .filter_map(|(region, range)| {
219 if region.code {
220 Some((
221 range.clone(),
222 HighlightStyle {
223 background_color: Some(code_span_background_color),
224 ..Default::default()
225 },
226 ))
227 } else {
228 None
229 }
230 }),
231 );
232
233 let mut links = Vec::new();
234 let mut link_ranges = Vec::new();
235 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
236 if let Some(link) = region.link.clone() {
237 links.push(link);
238 link_ranges.push(range.clone());
239 }
240 }
241
242 InteractiveText::new(
243 element_id,
244 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
245 )
246 .on_click(
247 link_ranges,
248 move |clicked_range_ix, window, cx| match &links[clicked_range_ix] {
249 markdown::Link::Web { url } => cx.open_url(url),
250 markdown::Link::Path { path } => {
251 if let Some(workspace) = &workspace {
252 _ = workspace.update(cx, |workspace, cx| {
253 workspace
254 .open_abs_path(path.clone(), false, window, cx)
255 .detach();
256 });
257 }
258 }
259 },
260 )
261}
262
263#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
264pub enum InlayId {
265 InlineCompletion(usize),
266 Hint(usize),
267}
268
269impl InlayId {
270 fn id(&self) -> usize {
271 match self {
272 Self::InlineCompletion(id) => *id,
273 Self::Hint(id) => *id,
274 }
275 }
276}
277
278enum DocumentHighlightRead {}
279enum DocumentHighlightWrite {}
280enum InputComposition {}
281
282#[derive(Debug, Copy, Clone, PartialEq, Eq)]
283pub enum Navigated {
284 Yes,
285 No,
286}
287
288impl Navigated {
289 pub fn from_bool(yes: bool) -> Navigated {
290 if yes {
291 Navigated::Yes
292 } else {
293 Navigated::No
294 }
295 }
296}
297
298pub fn init_settings(cx: &mut App) {
299 EditorSettings::register(cx);
300}
301
302pub fn init(cx: &mut App) {
303 init_settings(cx);
304
305 workspace::register_project_item::<Editor>(cx);
306 workspace::FollowableViewRegistry::register::<Editor>(cx);
307 workspace::register_serializable_item::<Editor>(cx);
308
309 cx.observe_new(
310 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
311 workspace.register_action(Editor::new_file);
312 workspace.register_action(Editor::new_file_vertical);
313 workspace.register_action(Editor::new_file_horizontal);
314 workspace.register_action(Editor::cancel_language_server_work);
315 },
316 )
317 .detach();
318
319 cx.on_action(move |_: &workspace::NewFile, cx| {
320 let app_state = workspace::AppState::global(cx);
321 if let Some(app_state) = app_state.upgrade() {
322 workspace::open_new(
323 Default::default(),
324 app_state,
325 cx,
326 |workspace, window, cx| {
327 Editor::new_file(workspace, &Default::default(), window, cx)
328 },
329 )
330 .detach();
331 }
332 });
333 cx.on_action(move |_: &workspace::NewWindow, cx| {
334 let app_state = workspace::AppState::global(cx);
335 if let Some(app_state) = app_state.upgrade() {
336 workspace::open_new(
337 Default::default(),
338 app_state,
339 cx,
340 |workspace, window, cx| {
341 cx.activate(true);
342 Editor::new_file(workspace, &Default::default(), window, cx)
343 },
344 )
345 .detach();
346 }
347 });
348}
349
350pub struct SearchWithinRange;
351
352trait InvalidationRegion {
353 fn ranges(&self) -> &[Range<Anchor>];
354}
355
356#[derive(Clone, Debug, PartialEq)]
357pub enum SelectPhase {
358 Begin {
359 position: DisplayPoint,
360 add: bool,
361 click_count: usize,
362 },
363 BeginColumnar {
364 position: DisplayPoint,
365 reset: bool,
366 goal_column: u32,
367 },
368 Extend {
369 position: DisplayPoint,
370 click_count: usize,
371 },
372 Update {
373 position: DisplayPoint,
374 goal_column: u32,
375 scroll_delta: gpui::Point<f32>,
376 },
377 End,
378}
379
380#[derive(Clone, Debug)]
381pub enum SelectMode {
382 Character,
383 Word(Range<Anchor>),
384 Line(Range<Anchor>),
385 All,
386}
387
388#[derive(Copy, Clone, PartialEq, Eq, Debug)]
389pub enum EditorMode {
390 SingleLine { auto_width: bool },
391 AutoHeight { max_lines: usize },
392 Full,
393}
394
395#[derive(Copy, Clone, Debug)]
396pub enum SoftWrap {
397 /// Prefer not to wrap at all.
398 ///
399 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
400 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
401 GitDiff,
402 /// Prefer a single line generally, unless an overly long line is encountered.
403 None,
404 /// Soft wrap lines that exceed the editor width.
405 EditorWidth,
406 /// Soft wrap lines at the preferred line length.
407 Column(u32),
408 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
409 Bounded(u32),
410}
411
412#[derive(Clone)]
413pub struct EditorStyle {
414 pub background: Hsla,
415 pub local_player: PlayerColor,
416 pub text: TextStyle,
417 pub scrollbar_width: Pixels,
418 pub syntax: Arc<SyntaxTheme>,
419 pub status: StatusColors,
420 pub inlay_hints_style: HighlightStyle,
421 pub inline_completion_styles: InlineCompletionStyles,
422 pub unnecessary_code_fade: f32,
423}
424
425impl Default for EditorStyle {
426 fn default() -> Self {
427 Self {
428 background: Hsla::default(),
429 local_player: PlayerColor::default(),
430 text: TextStyle::default(),
431 scrollbar_width: Pixels::default(),
432 syntax: Default::default(),
433 // HACK: Status colors don't have a real default.
434 // We should look into removing the status colors from the editor
435 // style and retrieve them directly from the theme.
436 status: StatusColors::dark(),
437 inlay_hints_style: HighlightStyle::default(),
438 inline_completion_styles: InlineCompletionStyles {
439 insertion: HighlightStyle::default(),
440 whitespace: HighlightStyle::default(),
441 },
442 unnecessary_code_fade: Default::default(),
443 }
444 }
445}
446
447pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
448 let show_background = language_settings::language_settings(None, None, cx)
449 .inlay_hints
450 .show_background;
451
452 HighlightStyle {
453 color: Some(cx.theme().status().hint),
454 background_color: show_background.then(|| cx.theme().status().hint_background),
455 ..HighlightStyle::default()
456 }
457}
458
459pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
460 InlineCompletionStyles {
461 insertion: HighlightStyle {
462 color: Some(cx.theme().status().predictive),
463 ..HighlightStyle::default()
464 },
465 whitespace: HighlightStyle {
466 background_color: Some(cx.theme().status().created_background),
467 ..HighlightStyle::default()
468 },
469 }
470}
471
472type CompletionId = usize;
473
474pub(crate) enum EditDisplayMode {
475 TabAccept,
476 DiffPopover,
477 Inline,
478}
479
480enum InlineCompletion {
481 Edit {
482 edits: Vec<(Range<Anchor>, String)>,
483 edit_preview: Option<EditPreview>,
484 display_mode: EditDisplayMode,
485 snapshot: BufferSnapshot,
486 },
487 Move {
488 target: Anchor,
489 range_around_target: Range<text::Anchor>,
490 snapshot: BufferSnapshot,
491 },
492}
493
494struct InlineCompletionState {
495 inlay_ids: Vec<InlayId>,
496 completion: InlineCompletion,
497 completion_id: Option<SharedString>,
498 invalidation_range: Range<Anchor>,
499}
500
501enum EditPredictionSettings {
502 Disabled,
503 Enabled {
504 show_in_menu: bool,
505 preview_requires_modifier: bool,
506 },
507}
508
509impl EditPredictionSettings {
510 pub fn is_enabled(&self) -> bool {
511 match self {
512 EditPredictionSettings::Disabled => false,
513 EditPredictionSettings::Enabled { .. } => true,
514 }
515 }
516}
517
518enum InlineCompletionHighlight {}
519
520pub enum MenuInlineCompletionsPolicy {
521 Never,
522 ByProvider,
523}
524
525#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
526struct EditorActionId(usize);
527
528impl EditorActionId {
529 pub fn post_inc(&mut self) -> Self {
530 let answer = self.0;
531
532 *self = Self(answer + 1);
533
534 Self(answer)
535 }
536}
537
538// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
539// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
540
541type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
542type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
543
544#[derive(Default)]
545struct ScrollbarMarkerState {
546 scrollbar_size: Size<Pixels>,
547 dirty: bool,
548 markers: Arc<[PaintQuad]>,
549 pending_refresh: Option<Task<Result<()>>>,
550}
551
552impl ScrollbarMarkerState {
553 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
554 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
555 }
556}
557
558#[derive(Clone, Debug)]
559struct RunnableTasks {
560 templates: Vec<(TaskSourceKind, TaskTemplate)>,
561 offset: MultiBufferOffset,
562 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
563 column: u32,
564 // Values of all named captures, including those starting with '_'
565 extra_variables: HashMap<String, String>,
566 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
567 context_range: Range<BufferOffset>,
568}
569
570impl RunnableTasks {
571 fn resolve<'a>(
572 &'a self,
573 cx: &'a task::TaskContext,
574 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
575 self.templates.iter().filter_map(|(kind, template)| {
576 template
577 .resolve_task(&kind.to_id_base(), cx)
578 .map(|task| (kind.clone(), task))
579 })
580 }
581}
582
583#[derive(Clone)]
584struct ResolvedTasks {
585 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
586 position: Anchor,
587}
588#[derive(Copy, Clone, Debug)]
589struct MultiBufferOffset(usize);
590#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
591struct BufferOffset(usize);
592
593// Addons allow storing per-editor state in other crates (e.g. Vim)
594pub trait Addon: 'static {
595 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
596
597 fn render_buffer_header_controls(
598 &self,
599 _: &ExcerptInfo,
600 _: &Window,
601 _: &App,
602 ) -> Option<AnyElement> {
603 None
604 }
605
606 fn to_any(&self) -> &dyn std::any::Any;
607}
608
609#[derive(Debug, Copy, Clone, PartialEq, Eq)]
610pub enum IsVimMode {
611 Yes,
612 No,
613}
614
615/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
616///
617/// See the [module level documentation](self) for more information.
618pub struct Editor {
619 focus_handle: FocusHandle,
620 last_focused_descendant: Option<WeakFocusHandle>,
621 /// The text buffer being edited
622 buffer: Entity<MultiBuffer>,
623 /// Map of how text in the buffer should be displayed.
624 /// Handles soft wraps, folds, fake inlay text insertions, etc.
625 pub display_map: Entity<DisplayMap>,
626 pub selections: SelectionsCollection,
627 pub scroll_manager: ScrollManager,
628 /// When inline assist editors are linked, they all render cursors because
629 /// typing enters text into each of them, even the ones that aren't focused.
630 pub(crate) show_cursor_when_unfocused: bool,
631 columnar_selection_tail: Option<Anchor>,
632 add_selections_state: Option<AddSelectionsState>,
633 select_next_state: Option<SelectNextState>,
634 select_prev_state: Option<SelectNextState>,
635 selection_history: SelectionHistory,
636 autoclose_regions: Vec<AutocloseRegion>,
637 snippet_stack: InvalidationStack<SnippetState>,
638 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
639 ime_transaction: Option<TransactionId>,
640 active_diagnostics: Option<ActiveDiagnosticGroup>,
641 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
642
643 // TODO: make this a access method
644 pub project: Option<Entity<Project>>,
645 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
646 completion_provider: Option<Box<dyn CompletionProvider>>,
647 collaboration_hub: Option<Box<dyn CollaborationHub>>,
648 blink_manager: Entity<BlinkManager>,
649 show_cursor_names: bool,
650 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
651 pub show_local_selections: bool,
652 mode: EditorMode,
653 show_breadcrumbs: bool,
654 show_gutter: bool,
655 show_scrollbars: bool,
656 show_line_numbers: Option<bool>,
657 use_relative_line_numbers: Option<bool>,
658 show_git_diff_gutter: Option<bool>,
659 show_code_actions: Option<bool>,
660 show_runnables: Option<bool>,
661 show_wrap_guides: Option<bool>,
662 show_indent_guides: Option<bool>,
663 placeholder_text: Option<Arc<str>>,
664 highlight_order: usize,
665 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
666 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
667 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
668 scrollbar_marker_state: ScrollbarMarkerState,
669 active_indent_guides_state: ActiveIndentGuidesState,
670 nav_history: Option<ItemNavHistory>,
671 context_menu: RefCell<Option<CodeContextMenu>>,
672 mouse_context_menu: Option<MouseContextMenu>,
673 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
674 signature_help_state: SignatureHelpState,
675 auto_signature_help: Option<bool>,
676 find_all_references_task_sources: Vec<Anchor>,
677 next_completion_id: CompletionId,
678 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
679 code_actions_task: Option<Task<Result<()>>>,
680 document_highlights_task: Option<Task<()>>,
681 linked_editing_range_task: Option<Task<Option<()>>>,
682 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
683 pending_rename: Option<RenameState>,
684 searchable: bool,
685 cursor_shape: CursorShape,
686 current_line_highlight: Option<CurrentLineHighlight>,
687 collapse_matches: bool,
688 autoindent_mode: Option<AutoindentMode>,
689 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
690 input_enabled: bool,
691 use_modal_editing: bool,
692 read_only: bool,
693 leader_peer_id: Option<PeerId>,
694 remote_id: Option<ViewId>,
695 hover_state: HoverState,
696 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
697 gutter_hovered: bool,
698 hovered_link_state: Option<HoveredLinkState>,
699 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
700 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
701 active_inline_completion: Option<InlineCompletionState>,
702 /// Used to prevent flickering as the user types while the menu is open
703 stale_inline_completion_in_menu: Option<InlineCompletionState>,
704 edit_prediction_settings: EditPredictionSettings,
705 inline_completions_hidden_for_vim_mode: bool,
706 show_inline_completions_override: Option<bool>,
707 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
708 previewing_inline_completion: bool,
709 inlay_hint_cache: InlayHintCache,
710 next_inlay_id: usize,
711 _subscriptions: Vec<Subscription>,
712 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
713 gutter_dimensions: GutterDimensions,
714 style: Option<EditorStyle>,
715 text_style_refinement: Option<TextStyleRefinement>,
716 next_editor_action_id: EditorActionId,
717 editor_actions:
718 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
719 use_autoclose: bool,
720 use_auto_surround: bool,
721 auto_replace_emoji_shortcode: bool,
722 show_git_blame_gutter: bool,
723 show_git_blame_inline: bool,
724 show_git_blame_inline_delay_task: Option<Task<()>>,
725 git_blame_inline_enabled: bool,
726 serialize_dirty_buffers: bool,
727 show_selection_menu: Option<bool>,
728 blame: Option<Entity<GitBlame>>,
729 blame_subscription: Option<Subscription>,
730 custom_context_menu: Option<
731 Box<
732 dyn 'static
733 + Fn(
734 &mut Self,
735 DisplayPoint,
736 &mut Window,
737 &mut Context<Self>,
738 ) -> Option<Entity<ui::ContextMenu>>,
739 >,
740 >,
741 last_bounds: Option<Bounds<Pixels>>,
742 last_position_map: Option<Rc<PositionMap>>,
743 expect_bounds_change: Option<Bounds<Pixels>>,
744 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
745 tasks_update_task: Option<Task<()>>,
746 in_project_search: bool,
747 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
748 breadcrumb_header: Option<String>,
749 focused_block: Option<FocusedBlock>,
750 next_scroll_position: NextScrollCursorCenterTopBottom,
751 addons: HashMap<TypeId, Box<dyn Addon>>,
752 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
753 selection_mark_mode: bool,
754 toggle_fold_multiple_buffers: Task<()>,
755 _scroll_cursor_center_top_bottom_task: Task<()>,
756}
757
758#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
759enum NextScrollCursorCenterTopBottom {
760 #[default]
761 Center,
762 Top,
763 Bottom,
764}
765
766impl NextScrollCursorCenterTopBottom {
767 fn next(&self) -> Self {
768 match self {
769 Self::Center => Self::Top,
770 Self::Top => Self::Bottom,
771 Self::Bottom => Self::Center,
772 }
773 }
774}
775
776#[derive(Clone)]
777pub struct EditorSnapshot {
778 pub mode: EditorMode,
779 show_gutter: bool,
780 show_line_numbers: Option<bool>,
781 show_git_diff_gutter: Option<bool>,
782 show_code_actions: Option<bool>,
783 show_runnables: Option<bool>,
784 git_blame_gutter_max_author_length: Option<usize>,
785 pub display_snapshot: DisplaySnapshot,
786 pub placeholder_text: Option<Arc<str>>,
787 is_focused: bool,
788 scroll_anchor: ScrollAnchor,
789 ongoing_scroll: OngoingScroll,
790 current_line_highlight: CurrentLineHighlight,
791 gutter_hovered: bool,
792}
793
794const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
795
796#[derive(Default, Debug, Clone, Copy)]
797pub struct GutterDimensions {
798 pub left_padding: Pixels,
799 pub right_padding: Pixels,
800 pub width: Pixels,
801 pub margin: Pixels,
802 pub git_blame_entries_width: Option<Pixels>,
803}
804
805impl GutterDimensions {
806 /// The full width of the space taken up by the gutter.
807 pub fn full_width(&self) -> Pixels {
808 self.margin + self.width
809 }
810
811 /// The width of the space reserved for the fold indicators,
812 /// use alongside 'justify_end' and `gutter_width` to
813 /// right align content with the line numbers
814 pub fn fold_area_width(&self) -> Pixels {
815 self.margin + self.right_padding
816 }
817}
818
819#[derive(Debug)]
820pub struct RemoteSelection {
821 pub replica_id: ReplicaId,
822 pub selection: Selection<Anchor>,
823 pub cursor_shape: CursorShape,
824 pub peer_id: PeerId,
825 pub line_mode: bool,
826 pub participant_index: Option<ParticipantIndex>,
827 pub user_name: Option<SharedString>,
828}
829
830#[derive(Clone, Debug)]
831struct SelectionHistoryEntry {
832 selections: Arc<[Selection<Anchor>]>,
833 select_next_state: Option<SelectNextState>,
834 select_prev_state: Option<SelectNextState>,
835 add_selections_state: Option<AddSelectionsState>,
836}
837
838enum SelectionHistoryMode {
839 Normal,
840 Undoing,
841 Redoing,
842}
843
844#[derive(Clone, PartialEq, Eq, Hash)]
845struct HoveredCursor {
846 replica_id: u16,
847 selection_id: usize,
848}
849
850impl Default for SelectionHistoryMode {
851 fn default() -> Self {
852 Self::Normal
853 }
854}
855
856#[derive(Default)]
857struct SelectionHistory {
858 #[allow(clippy::type_complexity)]
859 selections_by_transaction:
860 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
861 mode: SelectionHistoryMode,
862 undo_stack: VecDeque<SelectionHistoryEntry>,
863 redo_stack: VecDeque<SelectionHistoryEntry>,
864}
865
866impl SelectionHistory {
867 fn insert_transaction(
868 &mut self,
869 transaction_id: TransactionId,
870 selections: Arc<[Selection<Anchor>]>,
871 ) {
872 self.selections_by_transaction
873 .insert(transaction_id, (selections, None));
874 }
875
876 #[allow(clippy::type_complexity)]
877 fn transaction(
878 &self,
879 transaction_id: TransactionId,
880 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
881 self.selections_by_transaction.get(&transaction_id)
882 }
883
884 #[allow(clippy::type_complexity)]
885 fn transaction_mut(
886 &mut self,
887 transaction_id: TransactionId,
888 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
889 self.selections_by_transaction.get_mut(&transaction_id)
890 }
891
892 fn push(&mut self, entry: SelectionHistoryEntry) {
893 if !entry.selections.is_empty() {
894 match self.mode {
895 SelectionHistoryMode::Normal => {
896 self.push_undo(entry);
897 self.redo_stack.clear();
898 }
899 SelectionHistoryMode::Undoing => self.push_redo(entry),
900 SelectionHistoryMode::Redoing => self.push_undo(entry),
901 }
902 }
903 }
904
905 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
906 if self
907 .undo_stack
908 .back()
909 .map_or(true, |e| e.selections != entry.selections)
910 {
911 self.undo_stack.push_back(entry);
912 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
913 self.undo_stack.pop_front();
914 }
915 }
916 }
917
918 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
919 if self
920 .redo_stack
921 .back()
922 .map_or(true, |e| e.selections != entry.selections)
923 {
924 self.redo_stack.push_back(entry);
925 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
926 self.redo_stack.pop_front();
927 }
928 }
929 }
930}
931
932struct RowHighlight {
933 index: usize,
934 range: Range<Anchor>,
935 color: Hsla,
936 should_autoscroll: bool,
937}
938
939#[derive(Clone, Debug)]
940struct AddSelectionsState {
941 above: bool,
942 stack: Vec<usize>,
943}
944
945#[derive(Clone)]
946struct SelectNextState {
947 query: AhoCorasick,
948 wordwise: bool,
949 done: bool,
950}
951
952impl std::fmt::Debug for SelectNextState {
953 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
954 f.debug_struct(std::any::type_name::<Self>())
955 .field("wordwise", &self.wordwise)
956 .field("done", &self.done)
957 .finish()
958 }
959}
960
961#[derive(Debug)]
962struct AutocloseRegion {
963 selection_id: usize,
964 range: Range<Anchor>,
965 pair: BracketPair,
966}
967
968#[derive(Debug)]
969struct SnippetState {
970 ranges: Vec<Vec<Range<Anchor>>>,
971 active_index: usize,
972 choices: Vec<Option<Vec<String>>>,
973}
974
975#[doc(hidden)]
976pub struct RenameState {
977 pub range: Range<Anchor>,
978 pub old_name: Arc<str>,
979 pub editor: Entity<Editor>,
980 block_id: CustomBlockId,
981}
982
983struct InvalidationStack<T>(Vec<T>);
984
985struct RegisteredInlineCompletionProvider {
986 provider: Arc<dyn InlineCompletionProviderHandle>,
987 _subscription: Subscription,
988}
989
990#[derive(Debug)]
991struct ActiveDiagnosticGroup {
992 primary_range: Range<Anchor>,
993 primary_message: String,
994 group_id: usize,
995 blocks: HashMap<CustomBlockId, Diagnostic>,
996 is_valid: bool,
997}
998
999#[derive(Serialize, Deserialize, Clone, Debug)]
1000pub struct ClipboardSelection {
1001 pub len: usize,
1002 pub is_entire_line: bool,
1003 pub first_line_indent: u32,
1004}
1005
1006#[derive(Debug)]
1007pub(crate) struct NavigationData {
1008 cursor_anchor: Anchor,
1009 cursor_position: Point,
1010 scroll_anchor: ScrollAnchor,
1011 scroll_top_row: u32,
1012}
1013
1014#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1015pub enum GotoDefinitionKind {
1016 Symbol,
1017 Declaration,
1018 Type,
1019 Implementation,
1020}
1021
1022#[derive(Debug, Clone)]
1023enum InlayHintRefreshReason {
1024 Toggle(bool),
1025 SettingsChange(InlayHintSettings),
1026 NewLinesShown,
1027 BufferEdited(HashSet<Arc<Language>>),
1028 RefreshRequested,
1029 ExcerptsRemoved(Vec<ExcerptId>),
1030}
1031
1032impl InlayHintRefreshReason {
1033 fn description(&self) -> &'static str {
1034 match self {
1035 Self::Toggle(_) => "toggle",
1036 Self::SettingsChange(_) => "settings change",
1037 Self::NewLinesShown => "new lines shown",
1038 Self::BufferEdited(_) => "buffer edited",
1039 Self::RefreshRequested => "refresh requested",
1040 Self::ExcerptsRemoved(_) => "excerpts removed",
1041 }
1042 }
1043}
1044
1045pub enum FormatTarget {
1046 Buffers,
1047 Ranges(Vec<Range<MultiBufferPoint>>),
1048}
1049
1050pub(crate) struct FocusedBlock {
1051 id: BlockId,
1052 focus_handle: WeakFocusHandle,
1053}
1054
1055#[derive(Clone)]
1056enum JumpData {
1057 MultiBufferRow {
1058 row: MultiBufferRow,
1059 line_offset_from_top: u32,
1060 },
1061 MultiBufferPoint {
1062 excerpt_id: ExcerptId,
1063 position: Point,
1064 anchor: text::Anchor,
1065 line_offset_from_top: u32,
1066 },
1067}
1068
1069pub enum MultibufferSelectionMode {
1070 First,
1071 All,
1072}
1073
1074impl Editor {
1075 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1076 let buffer = cx.new(|cx| Buffer::local("", cx));
1077 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1078 Self::new(
1079 EditorMode::SingleLine { auto_width: false },
1080 buffer,
1081 None,
1082 false,
1083 window,
1084 cx,
1085 )
1086 }
1087
1088 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1089 let buffer = cx.new(|cx| Buffer::local("", cx));
1090 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1091 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1092 }
1093
1094 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1095 let buffer = cx.new(|cx| Buffer::local("", cx));
1096 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1097 Self::new(
1098 EditorMode::SingleLine { auto_width: true },
1099 buffer,
1100 None,
1101 false,
1102 window,
1103 cx,
1104 )
1105 }
1106
1107 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1108 let buffer = cx.new(|cx| Buffer::local("", cx));
1109 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1110 Self::new(
1111 EditorMode::AutoHeight { max_lines },
1112 buffer,
1113 None,
1114 false,
1115 window,
1116 cx,
1117 )
1118 }
1119
1120 pub fn for_buffer(
1121 buffer: Entity<Buffer>,
1122 project: Option<Entity<Project>>,
1123 window: &mut Window,
1124 cx: &mut Context<Self>,
1125 ) -> Self {
1126 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1127 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1128 }
1129
1130 pub fn for_multibuffer(
1131 buffer: Entity<MultiBuffer>,
1132 project: Option<Entity<Project>>,
1133 show_excerpt_controls: bool,
1134 window: &mut Window,
1135 cx: &mut Context<Self>,
1136 ) -> Self {
1137 Self::new(
1138 EditorMode::Full,
1139 buffer,
1140 project,
1141 show_excerpt_controls,
1142 window,
1143 cx,
1144 )
1145 }
1146
1147 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1148 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1149 let mut clone = Self::new(
1150 self.mode,
1151 self.buffer.clone(),
1152 self.project.clone(),
1153 show_excerpt_controls,
1154 window,
1155 cx,
1156 );
1157 self.display_map.update(cx, |display_map, cx| {
1158 let snapshot = display_map.snapshot(cx);
1159 clone.display_map.update(cx, |display_map, cx| {
1160 display_map.set_state(&snapshot, cx);
1161 });
1162 });
1163 clone.selections.clone_state(&self.selections);
1164 clone.scroll_manager.clone_state(&self.scroll_manager);
1165 clone.searchable = self.searchable;
1166 clone
1167 }
1168
1169 pub fn new(
1170 mode: EditorMode,
1171 buffer: Entity<MultiBuffer>,
1172 project: Option<Entity<Project>>,
1173 show_excerpt_controls: bool,
1174 window: &mut Window,
1175 cx: &mut Context<Self>,
1176 ) -> Self {
1177 let style = window.text_style();
1178 let font_size = style.font_size.to_pixels(window.rem_size());
1179 let editor = cx.entity().downgrade();
1180 let fold_placeholder = FoldPlaceholder {
1181 constrain_width: true,
1182 render: Arc::new(move |fold_id, fold_range, _, cx| {
1183 let editor = editor.clone();
1184 div()
1185 .id(fold_id)
1186 .bg(cx.theme().colors().ghost_element_background)
1187 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1188 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1189 .rounded_sm()
1190 .size_full()
1191 .cursor_pointer()
1192 .child("⋯")
1193 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1194 .on_click(move |_, _window, cx| {
1195 editor
1196 .update(cx, |editor, cx| {
1197 editor.unfold_ranges(
1198 &[fold_range.start..fold_range.end],
1199 true,
1200 false,
1201 cx,
1202 );
1203 cx.stop_propagation();
1204 })
1205 .ok();
1206 })
1207 .into_any()
1208 }),
1209 merge_adjacent: true,
1210 ..Default::default()
1211 };
1212 let display_map = cx.new(|cx| {
1213 DisplayMap::new(
1214 buffer.clone(),
1215 style.font(),
1216 font_size,
1217 None,
1218 show_excerpt_controls,
1219 FILE_HEADER_HEIGHT,
1220 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1221 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1222 fold_placeholder,
1223 cx,
1224 )
1225 });
1226
1227 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1228
1229 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1230
1231 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1232 .then(|| language_settings::SoftWrap::None);
1233
1234 let mut project_subscriptions = Vec::new();
1235 if mode == EditorMode::Full {
1236 if let Some(project) = project.as_ref() {
1237 if buffer.read(cx).is_singleton() {
1238 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1239 cx.emit(EditorEvent::TitleChanged);
1240 }));
1241 }
1242 project_subscriptions.push(cx.subscribe_in(
1243 project,
1244 window,
1245 |editor, _, event, window, cx| {
1246 if let project::Event::RefreshInlayHints = event {
1247 editor
1248 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1249 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1250 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1251 let focus_handle = editor.focus_handle(cx);
1252 if focus_handle.is_focused(window) {
1253 let snapshot = buffer.read(cx).snapshot();
1254 for (range, snippet) in snippet_edits {
1255 let editor_range =
1256 language::range_from_lsp(*range).to_offset(&snapshot);
1257 editor
1258 .insert_snippet(
1259 &[editor_range],
1260 snippet.clone(),
1261 window,
1262 cx,
1263 )
1264 .ok();
1265 }
1266 }
1267 }
1268 }
1269 },
1270 ));
1271 if let Some(task_inventory) = project
1272 .read(cx)
1273 .task_store()
1274 .read(cx)
1275 .task_inventory()
1276 .cloned()
1277 {
1278 project_subscriptions.push(cx.observe_in(
1279 &task_inventory,
1280 window,
1281 |editor, _, window, cx| {
1282 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1283 },
1284 ));
1285 }
1286 }
1287 }
1288
1289 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1290
1291 let inlay_hint_settings =
1292 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1293 let focus_handle = cx.focus_handle();
1294 cx.on_focus(&focus_handle, window, Self::handle_focus)
1295 .detach();
1296 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1297 .detach();
1298 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1299 .detach();
1300 cx.on_blur(&focus_handle, window, Self::handle_blur)
1301 .detach();
1302
1303 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1304 Some(false)
1305 } else {
1306 None
1307 };
1308
1309 let mut code_action_providers = Vec::new();
1310 if let Some(project) = project.clone() {
1311 get_uncommitted_diff_for_buffer(
1312 &project,
1313 buffer.read(cx).all_buffers(),
1314 buffer.clone(),
1315 cx,
1316 );
1317 code_action_providers.push(Rc::new(project) as Rc<_>);
1318 }
1319
1320 let mut this = Self {
1321 focus_handle,
1322 show_cursor_when_unfocused: false,
1323 last_focused_descendant: None,
1324 buffer: buffer.clone(),
1325 display_map: display_map.clone(),
1326 selections,
1327 scroll_manager: ScrollManager::new(cx),
1328 columnar_selection_tail: None,
1329 add_selections_state: None,
1330 select_next_state: None,
1331 select_prev_state: None,
1332 selection_history: Default::default(),
1333 autoclose_regions: Default::default(),
1334 snippet_stack: Default::default(),
1335 select_larger_syntax_node_stack: Vec::new(),
1336 ime_transaction: Default::default(),
1337 active_diagnostics: None,
1338 soft_wrap_mode_override,
1339 completion_provider: project.clone().map(|project| Box::new(project) as _),
1340 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1341 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1342 project,
1343 blink_manager: blink_manager.clone(),
1344 show_local_selections: true,
1345 show_scrollbars: true,
1346 mode,
1347 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1348 show_gutter: mode == EditorMode::Full,
1349 show_line_numbers: None,
1350 use_relative_line_numbers: None,
1351 show_git_diff_gutter: None,
1352 show_code_actions: None,
1353 show_runnables: None,
1354 show_wrap_guides: None,
1355 show_indent_guides,
1356 placeholder_text: None,
1357 highlight_order: 0,
1358 highlighted_rows: HashMap::default(),
1359 background_highlights: Default::default(),
1360 gutter_highlights: TreeMap::default(),
1361 scrollbar_marker_state: ScrollbarMarkerState::default(),
1362 active_indent_guides_state: ActiveIndentGuidesState::default(),
1363 nav_history: None,
1364 context_menu: RefCell::new(None),
1365 mouse_context_menu: None,
1366 completion_tasks: Default::default(),
1367 signature_help_state: SignatureHelpState::default(),
1368 auto_signature_help: None,
1369 find_all_references_task_sources: Vec::new(),
1370 next_completion_id: 0,
1371 next_inlay_id: 0,
1372 code_action_providers,
1373 available_code_actions: Default::default(),
1374 code_actions_task: Default::default(),
1375 document_highlights_task: Default::default(),
1376 linked_editing_range_task: Default::default(),
1377 pending_rename: Default::default(),
1378 searchable: true,
1379 cursor_shape: EditorSettings::get_global(cx)
1380 .cursor_shape
1381 .unwrap_or_default(),
1382 current_line_highlight: None,
1383 autoindent_mode: Some(AutoindentMode::EachLine),
1384 collapse_matches: false,
1385 workspace: None,
1386 input_enabled: true,
1387 use_modal_editing: mode == EditorMode::Full,
1388 read_only: false,
1389 use_autoclose: true,
1390 use_auto_surround: true,
1391 auto_replace_emoji_shortcode: false,
1392 leader_peer_id: None,
1393 remote_id: None,
1394 hover_state: Default::default(),
1395 pending_mouse_down: None,
1396 hovered_link_state: Default::default(),
1397 edit_prediction_provider: None,
1398 active_inline_completion: None,
1399 stale_inline_completion_in_menu: None,
1400 previewing_inline_completion: false,
1401 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1402
1403 gutter_hovered: false,
1404 pixel_position_of_newest_cursor: None,
1405 last_bounds: None,
1406 last_position_map: None,
1407 expect_bounds_change: None,
1408 gutter_dimensions: GutterDimensions::default(),
1409 style: None,
1410 show_cursor_names: false,
1411 hovered_cursors: Default::default(),
1412 next_editor_action_id: EditorActionId::default(),
1413 editor_actions: Rc::default(),
1414 inline_completions_hidden_for_vim_mode: false,
1415 show_inline_completions_override: None,
1416 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1417 edit_prediction_settings: EditPredictionSettings::Disabled,
1418 custom_context_menu: None,
1419 show_git_blame_gutter: false,
1420 show_git_blame_inline: false,
1421 show_selection_menu: None,
1422 show_git_blame_inline_delay_task: None,
1423 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1424 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1425 .session
1426 .restore_unsaved_buffers,
1427 blame: None,
1428 blame_subscription: None,
1429 tasks: Default::default(),
1430 _subscriptions: vec![
1431 cx.observe(&buffer, Self::on_buffer_changed),
1432 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1433 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1434 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1435 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1436 cx.observe_window_activation(window, |editor, window, cx| {
1437 let active = window.is_window_active();
1438 editor.blink_manager.update(cx, |blink_manager, cx| {
1439 if active {
1440 blink_manager.enable(cx);
1441 } else {
1442 blink_manager.disable(cx);
1443 }
1444 });
1445 }),
1446 ],
1447 tasks_update_task: None,
1448 linked_edit_ranges: Default::default(),
1449 in_project_search: false,
1450 previous_search_ranges: None,
1451 breadcrumb_header: None,
1452 focused_block: None,
1453 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1454 addons: HashMap::default(),
1455 registered_buffers: HashMap::default(),
1456 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1457 selection_mark_mode: false,
1458 toggle_fold_multiple_buffers: Task::ready(()),
1459 text_style_refinement: None,
1460 };
1461 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1462 this._subscriptions.extend(project_subscriptions);
1463
1464 this.end_selection(window, cx);
1465 this.scroll_manager.show_scrollbar(window, cx);
1466
1467 if mode == EditorMode::Full {
1468 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1469 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1470
1471 if this.git_blame_inline_enabled {
1472 this.git_blame_inline_enabled = true;
1473 this.start_git_blame_inline(false, window, cx);
1474 }
1475
1476 if let Some(buffer) = buffer.read(cx).as_singleton() {
1477 if let Some(project) = this.project.as_ref() {
1478 let lsp_store = project.read(cx).lsp_store();
1479 let handle = lsp_store.update(cx, |lsp_store, cx| {
1480 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1481 });
1482 this.registered_buffers
1483 .insert(buffer.read(cx).remote_id(), handle);
1484 }
1485 }
1486 }
1487
1488 this.report_editor_event("Editor Opened", None, cx);
1489 this
1490 }
1491
1492 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1493 self.mouse_context_menu
1494 .as_ref()
1495 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1496 }
1497
1498 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1499 let mut key_context = KeyContext::new_with_defaults();
1500 key_context.add("Editor");
1501 let mode = match self.mode {
1502 EditorMode::SingleLine { .. } => "single_line",
1503 EditorMode::AutoHeight { .. } => "auto_height",
1504 EditorMode::Full => "full",
1505 };
1506
1507 if EditorSettings::jupyter_enabled(cx) {
1508 key_context.add("jupyter");
1509 }
1510
1511 key_context.set("mode", mode);
1512 if self.pending_rename.is_some() {
1513 key_context.add("renaming");
1514 }
1515
1516 let mut showing_completions = false;
1517
1518 match self.context_menu.borrow().as_ref() {
1519 Some(CodeContextMenu::Completions(_)) => {
1520 key_context.add("menu");
1521 key_context.add("showing_completions");
1522 showing_completions = true;
1523 }
1524 Some(CodeContextMenu::CodeActions(_)) => {
1525 key_context.add("menu");
1526 key_context.add("showing_code_actions")
1527 }
1528 None => {}
1529 }
1530
1531 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1532 if !self.focus_handle(cx).contains_focused(window, cx)
1533 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1534 {
1535 for addon in self.addons.values() {
1536 addon.extend_key_context(&mut key_context, cx)
1537 }
1538 }
1539
1540 if let Some(extension) = self
1541 .buffer
1542 .read(cx)
1543 .as_singleton()
1544 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1545 {
1546 key_context.set("extension", extension.to_string());
1547 }
1548
1549 if self.has_active_inline_completion() {
1550 key_context.add("copilot_suggestion");
1551 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1552
1553 if showing_completions || self.edit_prediction_requires_modifier() {
1554 key_context.add(EDIT_PREDICTION_REQUIRES_MODIFIER_KEY_CONTEXT);
1555 }
1556 }
1557
1558 if self.selection_mark_mode {
1559 key_context.add("selection_mode");
1560 }
1561
1562 key_context
1563 }
1564
1565 pub fn accept_edit_prediction_keybind(
1566 &self,
1567 window: &Window,
1568 cx: &App,
1569 ) -> AcceptEditPredictionBinding {
1570 let mut context = self.key_context(window, cx);
1571 context.add(EDIT_PREDICTION_KEY_CONTEXT);
1572
1573 AcceptEditPredictionBinding(
1574 window
1575 .bindings_for_action_in_context(&AcceptEditPrediction, context)
1576 .into_iter()
1577 .next(),
1578 )
1579 }
1580
1581 pub fn new_file(
1582 workspace: &mut Workspace,
1583 _: &workspace::NewFile,
1584 window: &mut Window,
1585 cx: &mut Context<Workspace>,
1586 ) {
1587 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1588 "Failed to create buffer",
1589 window,
1590 cx,
1591 |e, _, _| match e.error_code() {
1592 ErrorCode::RemoteUpgradeRequired => Some(format!(
1593 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1594 e.error_tag("required").unwrap_or("the latest version")
1595 )),
1596 _ => None,
1597 },
1598 );
1599 }
1600
1601 pub fn new_in_workspace(
1602 workspace: &mut Workspace,
1603 window: &mut Window,
1604 cx: &mut Context<Workspace>,
1605 ) -> Task<Result<Entity<Editor>>> {
1606 let project = workspace.project().clone();
1607 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1608
1609 cx.spawn_in(window, |workspace, mut cx| async move {
1610 let buffer = create.await?;
1611 workspace.update_in(&mut cx, |workspace, window, cx| {
1612 let editor =
1613 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1614 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1615 editor
1616 })
1617 })
1618 }
1619
1620 fn new_file_vertical(
1621 workspace: &mut Workspace,
1622 _: &workspace::NewFileSplitVertical,
1623 window: &mut Window,
1624 cx: &mut Context<Workspace>,
1625 ) {
1626 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1627 }
1628
1629 fn new_file_horizontal(
1630 workspace: &mut Workspace,
1631 _: &workspace::NewFileSplitHorizontal,
1632 window: &mut Window,
1633 cx: &mut Context<Workspace>,
1634 ) {
1635 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1636 }
1637
1638 fn new_file_in_direction(
1639 workspace: &mut Workspace,
1640 direction: SplitDirection,
1641 window: &mut Window,
1642 cx: &mut Context<Workspace>,
1643 ) {
1644 let project = workspace.project().clone();
1645 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1646
1647 cx.spawn_in(window, |workspace, mut cx| async move {
1648 let buffer = create.await?;
1649 workspace.update_in(&mut cx, move |workspace, window, cx| {
1650 workspace.split_item(
1651 direction,
1652 Box::new(
1653 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1654 ),
1655 window,
1656 cx,
1657 )
1658 })?;
1659 anyhow::Ok(())
1660 })
1661 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1662 match e.error_code() {
1663 ErrorCode::RemoteUpgradeRequired => Some(format!(
1664 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1665 e.error_tag("required").unwrap_or("the latest version")
1666 )),
1667 _ => None,
1668 }
1669 });
1670 }
1671
1672 pub fn leader_peer_id(&self) -> Option<PeerId> {
1673 self.leader_peer_id
1674 }
1675
1676 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1677 &self.buffer
1678 }
1679
1680 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1681 self.workspace.as_ref()?.0.upgrade()
1682 }
1683
1684 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1685 self.buffer().read(cx).title(cx)
1686 }
1687
1688 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1689 let git_blame_gutter_max_author_length = self
1690 .render_git_blame_gutter(cx)
1691 .then(|| {
1692 if let Some(blame) = self.blame.as_ref() {
1693 let max_author_length =
1694 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1695 Some(max_author_length)
1696 } else {
1697 None
1698 }
1699 })
1700 .flatten();
1701
1702 EditorSnapshot {
1703 mode: self.mode,
1704 show_gutter: self.show_gutter,
1705 show_line_numbers: self.show_line_numbers,
1706 show_git_diff_gutter: self.show_git_diff_gutter,
1707 show_code_actions: self.show_code_actions,
1708 show_runnables: self.show_runnables,
1709 git_blame_gutter_max_author_length,
1710 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1711 scroll_anchor: self.scroll_manager.anchor(),
1712 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1713 placeholder_text: self.placeholder_text.clone(),
1714 is_focused: self.focus_handle.is_focused(window),
1715 current_line_highlight: self
1716 .current_line_highlight
1717 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1718 gutter_hovered: self.gutter_hovered,
1719 }
1720 }
1721
1722 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1723 self.buffer.read(cx).language_at(point, cx)
1724 }
1725
1726 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1727 self.buffer.read(cx).read(cx).file_at(point).cloned()
1728 }
1729
1730 pub fn active_excerpt(
1731 &self,
1732 cx: &App,
1733 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1734 self.buffer
1735 .read(cx)
1736 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1737 }
1738
1739 pub fn mode(&self) -> EditorMode {
1740 self.mode
1741 }
1742
1743 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1744 self.collaboration_hub.as_deref()
1745 }
1746
1747 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1748 self.collaboration_hub = Some(hub);
1749 }
1750
1751 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1752 self.in_project_search = in_project_search;
1753 }
1754
1755 pub fn set_custom_context_menu(
1756 &mut self,
1757 f: impl 'static
1758 + Fn(
1759 &mut Self,
1760 DisplayPoint,
1761 &mut Window,
1762 &mut Context<Self>,
1763 ) -> Option<Entity<ui::ContextMenu>>,
1764 ) {
1765 self.custom_context_menu = Some(Box::new(f))
1766 }
1767
1768 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1769 self.completion_provider = provider;
1770 }
1771
1772 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1773 self.semantics_provider.clone()
1774 }
1775
1776 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1777 self.semantics_provider = provider;
1778 }
1779
1780 pub fn set_edit_prediction_provider<T>(
1781 &mut self,
1782 provider: Option<Entity<T>>,
1783 window: &mut Window,
1784 cx: &mut Context<Self>,
1785 ) where
1786 T: EditPredictionProvider,
1787 {
1788 self.edit_prediction_provider =
1789 provider.map(|provider| RegisteredInlineCompletionProvider {
1790 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1791 if this.focus_handle.is_focused(window) {
1792 this.update_visible_inline_completion(window, cx);
1793 }
1794 }),
1795 provider: Arc::new(provider),
1796 });
1797 self.refresh_inline_completion(false, false, window, cx);
1798 }
1799
1800 pub fn placeholder_text(&self) -> Option<&str> {
1801 self.placeholder_text.as_deref()
1802 }
1803
1804 pub fn set_placeholder_text(
1805 &mut self,
1806 placeholder_text: impl Into<Arc<str>>,
1807 cx: &mut Context<Self>,
1808 ) {
1809 let placeholder_text = Some(placeholder_text.into());
1810 if self.placeholder_text != placeholder_text {
1811 self.placeholder_text = placeholder_text;
1812 cx.notify();
1813 }
1814 }
1815
1816 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1817 self.cursor_shape = cursor_shape;
1818
1819 // Disrupt blink for immediate user feedback that the cursor shape has changed
1820 self.blink_manager.update(cx, BlinkManager::show_cursor);
1821
1822 cx.notify();
1823 }
1824
1825 pub fn set_current_line_highlight(
1826 &mut self,
1827 current_line_highlight: Option<CurrentLineHighlight>,
1828 ) {
1829 self.current_line_highlight = current_line_highlight;
1830 }
1831
1832 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1833 self.collapse_matches = collapse_matches;
1834 }
1835
1836 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1837 let buffers = self.buffer.read(cx).all_buffers();
1838 let Some(lsp_store) = self.lsp_store(cx) else {
1839 return;
1840 };
1841 lsp_store.update(cx, |lsp_store, cx| {
1842 for buffer in buffers {
1843 self.registered_buffers
1844 .entry(buffer.read(cx).remote_id())
1845 .or_insert_with(|| {
1846 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1847 });
1848 }
1849 })
1850 }
1851
1852 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1853 if self.collapse_matches {
1854 return range.start..range.start;
1855 }
1856 range.clone()
1857 }
1858
1859 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1860 if self.display_map.read(cx).clip_at_line_ends != clip {
1861 self.display_map
1862 .update(cx, |map, _| map.clip_at_line_ends = clip);
1863 }
1864 }
1865
1866 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1867 self.input_enabled = input_enabled;
1868 }
1869
1870 pub fn set_inline_completions_hidden_for_vim_mode(
1871 &mut self,
1872 hidden: bool,
1873 window: &mut Window,
1874 cx: &mut Context<Self>,
1875 ) {
1876 if hidden != self.inline_completions_hidden_for_vim_mode {
1877 self.inline_completions_hidden_for_vim_mode = hidden;
1878 if hidden {
1879 self.update_visible_inline_completion(window, cx);
1880 } else {
1881 self.refresh_inline_completion(true, false, window, cx);
1882 }
1883 }
1884 }
1885
1886 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1887 self.menu_inline_completions_policy = value;
1888 }
1889
1890 pub fn set_autoindent(&mut self, autoindent: bool) {
1891 if autoindent {
1892 self.autoindent_mode = Some(AutoindentMode::EachLine);
1893 } else {
1894 self.autoindent_mode = None;
1895 }
1896 }
1897
1898 pub fn read_only(&self, cx: &App) -> bool {
1899 self.read_only || self.buffer.read(cx).read_only()
1900 }
1901
1902 pub fn set_read_only(&mut self, read_only: bool) {
1903 self.read_only = read_only;
1904 }
1905
1906 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1907 self.use_autoclose = autoclose;
1908 }
1909
1910 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1911 self.use_auto_surround = auto_surround;
1912 }
1913
1914 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1915 self.auto_replace_emoji_shortcode = auto_replace;
1916 }
1917
1918 pub fn toggle_inline_completions(
1919 &mut self,
1920 _: &ToggleEditPrediction,
1921 window: &mut Window,
1922 cx: &mut Context<Self>,
1923 ) {
1924 if self.show_inline_completions_override.is_some() {
1925 self.set_show_edit_predictions(None, window, cx);
1926 } else {
1927 let show_edit_predictions = !self.edit_predictions_enabled();
1928 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1929 }
1930 }
1931
1932 pub fn set_show_edit_predictions(
1933 &mut self,
1934 show_edit_predictions: Option<bool>,
1935 window: &mut Window,
1936 cx: &mut Context<Self>,
1937 ) {
1938 self.show_inline_completions_override = show_edit_predictions;
1939 self.refresh_inline_completion(false, true, window, cx);
1940 }
1941
1942 pub fn inline_completion_start_anchor(&self) -> Option<Anchor> {
1943 let active_completion = self.active_inline_completion.as_ref()?;
1944 let result = match &active_completion.completion {
1945 InlineCompletion::Edit { edits, .. } => edits.first()?.0.start,
1946 InlineCompletion::Move { target, .. } => *target,
1947 };
1948 Some(result)
1949 }
1950
1951 fn inline_completions_disabled_in_scope(
1952 &self,
1953 buffer: &Entity<Buffer>,
1954 buffer_position: language::Anchor,
1955 cx: &App,
1956 ) -> bool {
1957 let snapshot = buffer.read(cx).snapshot();
1958 let settings = snapshot.settings_at(buffer_position, cx);
1959
1960 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1961 return false;
1962 };
1963
1964 scope.override_name().map_or(false, |scope_name| {
1965 settings
1966 .edit_predictions_disabled_in
1967 .iter()
1968 .any(|s| s == scope_name)
1969 })
1970 }
1971
1972 pub fn set_use_modal_editing(&mut self, to: bool) {
1973 self.use_modal_editing = to;
1974 }
1975
1976 pub fn use_modal_editing(&self) -> bool {
1977 self.use_modal_editing
1978 }
1979
1980 fn selections_did_change(
1981 &mut self,
1982 local: bool,
1983 old_cursor_position: &Anchor,
1984 show_completions: bool,
1985 window: &mut Window,
1986 cx: &mut Context<Self>,
1987 ) {
1988 window.invalidate_character_coordinates();
1989
1990 // Copy selections to primary selection buffer
1991 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1992 if local {
1993 let selections = self.selections.all::<usize>(cx);
1994 let buffer_handle = self.buffer.read(cx).read(cx);
1995
1996 let mut text = String::new();
1997 for (index, selection) in selections.iter().enumerate() {
1998 let text_for_selection = buffer_handle
1999 .text_for_range(selection.start..selection.end)
2000 .collect::<String>();
2001
2002 text.push_str(&text_for_selection);
2003 if index != selections.len() - 1 {
2004 text.push('\n');
2005 }
2006 }
2007
2008 if !text.is_empty() {
2009 cx.write_to_primary(ClipboardItem::new_string(text));
2010 }
2011 }
2012
2013 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2014 self.buffer.update(cx, |buffer, cx| {
2015 buffer.set_active_selections(
2016 &self.selections.disjoint_anchors(),
2017 self.selections.line_mode,
2018 self.cursor_shape,
2019 cx,
2020 )
2021 });
2022 }
2023 let display_map = self
2024 .display_map
2025 .update(cx, |display_map, cx| display_map.snapshot(cx));
2026 let buffer = &display_map.buffer_snapshot;
2027 self.add_selections_state = None;
2028 self.select_next_state = None;
2029 self.select_prev_state = None;
2030 self.select_larger_syntax_node_stack.clear();
2031 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2032 self.snippet_stack
2033 .invalidate(&self.selections.disjoint_anchors(), buffer);
2034 self.take_rename(false, window, cx);
2035
2036 let new_cursor_position = self.selections.newest_anchor().head();
2037
2038 self.push_to_nav_history(
2039 *old_cursor_position,
2040 Some(new_cursor_position.to_point(buffer)),
2041 cx,
2042 );
2043
2044 if local {
2045 let new_cursor_position = self.selections.newest_anchor().head();
2046 let mut context_menu = self.context_menu.borrow_mut();
2047 let completion_menu = match context_menu.as_ref() {
2048 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2049 _ => {
2050 *context_menu = None;
2051 None
2052 }
2053 };
2054 if let Some(buffer_id) = new_cursor_position.buffer_id {
2055 if !self.registered_buffers.contains_key(&buffer_id) {
2056 if let Some(lsp_store) = self.lsp_store(cx) {
2057 lsp_store.update(cx, |lsp_store, cx| {
2058 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2059 return;
2060 };
2061 self.registered_buffers.insert(
2062 buffer_id,
2063 lsp_store.register_buffer_with_language_servers(&buffer, cx),
2064 );
2065 })
2066 }
2067 }
2068 }
2069
2070 if let Some(completion_menu) = completion_menu {
2071 let cursor_position = new_cursor_position.to_offset(buffer);
2072 let (word_range, kind) =
2073 buffer.surrounding_word(completion_menu.initial_position, true);
2074 if kind == Some(CharKind::Word)
2075 && word_range.to_inclusive().contains(&cursor_position)
2076 {
2077 let mut completion_menu = completion_menu.clone();
2078 drop(context_menu);
2079
2080 let query = Self::completion_query(buffer, cursor_position);
2081 cx.spawn(move |this, mut cx| async move {
2082 completion_menu
2083 .filter(query.as_deref(), cx.background_executor().clone())
2084 .await;
2085
2086 this.update(&mut cx, |this, cx| {
2087 let mut context_menu = this.context_menu.borrow_mut();
2088 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2089 else {
2090 return;
2091 };
2092
2093 if menu.id > completion_menu.id {
2094 return;
2095 }
2096
2097 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2098 drop(context_menu);
2099 cx.notify();
2100 })
2101 })
2102 .detach();
2103
2104 if show_completions {
2105 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2106 }
2107 } else {
2108 drop(context_menu);
2109 self.hide_context_menu(window, cx);
2110 }
2111 } else {
2112 drop(context_menu);
2113 }
2114
2115 hide_hover(self, cx);
2116
2117 if old_cursor_position.to_display_point(&display_map).row()
2118 != new_cursor_position.to_display_point(&display_map).row()
2119 {
2120 self.available_code_actions.take();
2121 }
2122 self.refresh_code_actions(window, cx);
2123 self.refresh_document_highlights(cx);
2124 refresh_matching_bracket_highlights(self, window, cx);
2125 self.update_visible_inline_completion(window, cx);
2126 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2127 if self.git_blame_inline_enabled {
2128 self.start_inline_blame_timer(window, cx);
2129 }
2130 }
2131
2132 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2133 cx.emit(EditorEvent::SelectionsChanged { local });
2134
2135 if self.selections.disjoint_anchors().len() == 1 {
2136 cx.emit(SearchEvent::ActiveMatchChanged)
2137 }
2138 cx.notify();
2139 }
2140
2141 pub fn change_selections<R>(
2142 &mut self,
2143 autoscroll: Option<Autoscroll>,
2144 window: &mut Window,
2145 cx: &mut Context<Self>,
2146 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2147 ) -> R {
2148 self.change_selections_inner(autoscroll, true, window, cx, change)
2149 }
2150
2151 pub fn change_selections_inner<R>(
2152 &mut self,
2153 autoscroll: Option<Autoscroll>,
2154 request_completions: bool,
2155 window: &mut Window,
2156 cx: &mut Context<Self>,
2157 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2158 ) -> R {
2159 let old_cursor_position = self.selections.newest_anchor().head();
2160 self.push_to_selection_history();
2161
2162 let (changed, result) = self.selections.change_with(cx, change);
2163
2164 if changed {
2165 if let Some(autoscroll) = autoscroll {
2166 self.request_autoscroll(autoscroll, cx);
2167 }
2168 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2169
2170 if self.should_open_signature_help_automatically(
2171 &old_cursor_position,
2172 self.signature_help_state.backspace_pressed(),
2173 cx,
2174 ) {
2175 self.show_signature_help(&ShowSignatureHelp, window, cx);
2176 }
2177 self.signature_help_state.set_backspace_pressed(false);
2178 }
2179
2180 result
2181 }
2182
2183 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2184 where
2185 I: IntoIterator<Item = (Range<S>, T)>,
2186 S: ToOffset,
2187 T: Into<Arc<str>>,
2188 {
2189 if self.read_only(cx) {
2190 return;
2191 }
2192
2193 self.buffer
2194 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2195 }
2196
2197 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2198 where
2199 I: IntoIterator<Item = (Range<S>, T)>,
2200 S: ToOffset,
2201 T: Into<Arc<str>>,
2202 {
2203 if self.read_only(cx) {
2204 return;
2205 }
2206
2207 self.buffer.update(cx, |buffer, cx| {
2208 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2209 });
2210 }
2211
2212 pub fn edit_with_block_indent<I, S, T>(
2213 &mut self,
2214 edits: I,
2215 original_indent_columns: Vec<u32>,
2216 cx: &mut Context<Self>,
2217 ) where
2218 I: IntoIterator<Item = (Range<S>, T)>,
2219 S: ToOffset,
2220 T: Into<Arc<str>>,
2221 {
2222 if self.read_only(cx) {
2223 return;
2224 }
2225
2226 self.buffer.update(cx, |buffer, cx| {
2227 buffer.edit(
2228 edits,
2229 Some(AutoindentMode::Block {
2230 original_indent_columns,
2231 }),
2232 cx,
2233 )
2234 });
2235 }
2236
2237 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2238 self.hide_context_menu(window, cx);
2239
2240 match phase {
2241 SelectPhase::Begin {
2242 position,
2243 add,
2244 click_count,
2245 } => self.begin_selection(position, add, click_count, window, cx),
2246 SelectPhase::BeginColumnar {
2247 position,
2248 goal_column,
2249 reset,
2250 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2251 SelectPhase::Extend {
2252 position,
2253 click_count,
2254 } => self.extend_selection(position, click_count, window, cx),
2255 SelectPhase::Update {
2256 position,
2257 goal_column,
2258 scroll_delta,
2259 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2260 SelectPhase::End => self.end_selection(window, cx),
2261 }
2262 }
2263
2264 fn extend_selection(
2265 &mut self,
2266 position: DisplayPoint,
2267 click_count: usize,
2268 window: &mut Window,
2269 cx: &mut Context<Self>,
2270 ) {
2271 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2272 let tail = self.selections.newest::<usize>(cx).tail();
2273 self.begin_selection(position, false, click_count, window, cx);
2274
2275 let position = position.to_offset(&display_map, Bias::Left);
2276 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2277
2278 let mut pending_selection = self
2279 .selections
2280 .pending_anchor()
2281 .expect("extend_selection not called with pending selection");
2282 if position >= tail {
2283 pending_selection.start = tail_anchor;
2284 } else {
2285 pending_selection.end = tail_anchor;
2286 pending_selection.reversed = true;
2287 }
2288
2289 let mut pending_mode = self.selections.pending_mode().unwrap();
2290 match &mut pending_mode {
2291 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2292 _ => {}
2293 }
2294
2295 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2296 s.set_pending(pending_selection, pending_mode)
2297 });
2298 }
2299
2300 fn begin_selection(
2301 &mut self,
2302 position: DisplayPoint,
2303 add: bool,
2304 click_count: usize,
2305 window: &mut Window,
2306 cx: &mut Context<Self>,
2307 ) {
2308 if !self.focus_handle.is_focused(window) {
2309 self.last_focused_descendant = None;
2310 window.focus(&self.focus_handle);
2311 }
2312
2313 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2314 let buffer = &display_map.buffer_snapshot;
2315 let newest_selection = self.selections.newest_anchor().clone();
2316 let position = display_map.clip_point(position, Bias::Left);
2317
2318 let start;
2319 let end;
2320 let mode;
2321 let mut auto_scroll;
2322 match click_count {
2323 1 => {
2324 start = buffer.anchor_before(position.to_point(&display_map));
2325 end = start;
2326 mode = SelectMode::Character;
2327 auto_scroll = true;
2328 }
2329 2 => {
2330 let range = movement::surrounding_word(&display_map, position);
2331 start = buffer.anchor_before(range.start.to_point(&display_map));
2332 end = buffer.anchor_before(range.end.to_point(&display_map));
2333 mode = SelectMode::Word(start..end);
2334 auto_scroll = true;
2335 }
2336 3 => {
2337 let position = display_map
2338 .clip_point(position, Bias::Left)
2339 .to_point(&display_map);
2340 let line_start = display_map.prev_line_boundary(position).0;
2341 let next_line_start = buffer.clip_point(
2342 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2343 Bias::Left,
2344 );
2345 start = buffer.anchor_before(line_start);
2346 end = buffer.anchor_before(next_line_start);
2347 mode = SelectMode::Line(start..end);
2348 auto_scroll = true;
2349 }
2350 _ => {
2351 start = buffer.anchor_before(0);
2352 end = buffer.anchor_before(buffer.len());
2353 mode = SelectMode::All;
2354 auto_scroll = false;
2355 }
2356 }
2357 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2358
2359 let point_to_delete: Option<usize> = {
2360 let selected_points: Vec<Selection<Point>> =
2361 self.selections.disjoint_in_range(start..end, cx);
2362
2363 if !add || click_count > 1 {
2364 None
2365 } else if !selected_points.is_empty() {
2366 Some(selected_points[0].id)
2367 } else {
2368 let clicked_point_already_selected =
2369 self.selections.disjoint.iter().find(|selection| {
2370 selection.start.to_point(buffer) == start.to_point(buffer)
2371 || selection.end.to_point(buffer) == end.to_point(buffer)
2372 });
2373
2374 clicked_point_already_selected.map(|selection| selection.id)
2375 }
2376 };
2377
2378 let selections_count = self.selections.count();
2379
2380 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2381 if let Some(point_to_delete) = point_to_delete {
2382 s.delete(point_to_delete);
2383
2384 if selections_count == 1 {
2385 s.set_pending_anchor_range(start..end, mode);
2386 }
2387 } else {
2388 if !add {
2389 s.clear_disjoint();
2390 } else if click_count > 1 {
2391 s.delete(newest_selection.id)
2392 }
2393
2394 s.set_pending_anchor_range(start..end, mode);
2395 }
2396 });
2397 }
2398
2399 fn begin_columnar_selection(
2400 &mut self,
2401 position: DisplayPoint,
2402 goal_column: u32,
2403 reset: bool,
2404 window: &mut Window,
2405 cx: &mut Context<Self>,
2406 ) {
2407 if !self.focus_handle.is_focused(window) {
2408 self.last_focused_descendant = None;
2409 window.focus(&self.focus_handle);
2410 }
2411
2412 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2413
2414 if reset {
2415 let pointer_position = display_map
2416 .buffer_snapshot
2417 .anchor_before(position.to_point(&display_map));
2418
2419 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2420 s.clear_disjoint();
2421 s.set_pending_anchor_range(
2422 pointer_position..pointer_position,
2423 SelectMode::Character,
2424 );
2425 });
2426 }
2427
2428 let tail = self.selections.newest::<Point>(cx).tail();
2429 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2430
2431 if !reset {
2432 self.select_columns(
2433 tail.to_display_point(&display_map),
2434 position,
2435 goal_column,
2436 &display_map,
2437 window,
2438 cx,
2439 );
2440 }
2441 }
2442
2443 fn update_selection(
2444 &mut self,
2445 position: DisplayPoint,
2446 goal_column: u32,
2447 scroll_delta: gpui::Point<f32>,
2448 window: &mut Window,
2449 cx: &mut Context<Self>,
2450 ) {
2451 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2452
2453 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2454 let tail = tail.to_display_point(&display_map);
2455 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2456 } else if let Some(mut pending) = self.selections.pending_anchor() {
2457 let buffer = self.buffer.read(cx).snapshot(cx);
2458 let head;
2459 let tail;
2460 let mode = self.selections.pending_mode().unwrap();
2461 match &mode {
2462 SelectMode::Character => {
2463 head = position.to_point(&display_map);
2464 tail = pending.tail().to_point(&buffer);
2465 }
2466 SelectMode::Word(original_range) => {
2467 let original_display_range = original_range.start.to_display_point(&display_map)
2468 ..original_range.end.to_display_point(&display_map);
2469 let original_buffer_range = original_display_range.start.to_point(&display_map)
2470 ..original_display_range.end.to_point(&display_map);
2471 if movement::is_inside_word(&display_map, position)
2472 || original_display_range.contains(&position)
2473 {
2474 let word_range = movement::surrounding_word(&display_map, position);
2475 if word_range.start < original_display_range.start {
2476 head = word_range.start.to_point(&display_map);
2477 } else {
2478 head = word_range.end.to_point(&display_map);
2479 }
2480 } else {
2481 head = position.to_point(&display_map);
2482 }
2483
2484 if head <= original_buffer_range.start {
2485 tail = original_buffer_range.end;
2486 } else {
2487 tail = original_buffer_range.start;
2488 }
2489 }
2490 SelectMode::Line(original_range) => {
2491 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2492
2493 let position = display_map
2494 .clip_point(position, Bias::Left)
2495 .to_point(&display_map);
2496 let line_start = display_map.prev_line_boundary(position).0;
2497 let next_line_start = buffer.clip_point(
2498 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2499 Bias::Left,
2500 );
2501
2502 if line_start < original_range.start {
2503 head = line_start
2504 } else {
2505 head = next_line_start
2506 }
2507
2508 if head <= original_range.start {
2509 tail = original_range.end;
2510 } else {
2511 tail = original_range.start;
2512 }
2513 }
2514 SelectMode::All => {
2515 return;
2516 }
2517 };
2518
2519 if head < tail {
2520 pending.start = buffer.anchor_before(head);
2521 pending.end = buffer.anchor_before(tail);
2522 pending.reversed = true;
2523 } else {
2524 pending.start = buffer.anchor_before(tail);
2525 pending.end = buffer.anchor_before(head);
2526 pending.reversed = false;
2527 }
2528
2529 self.change_selections(None, window, cx, |s| {
2530 s.set_pending(pending, mode);
2531 });
2532 } else {
2533 log::error!("update_selection dispatched with no pending selection");
2534 return;
2535 }
2536
2537 self.apply_scroll_delta(scroll_delta, window, cx);
2538 cx.notify();
2539 }
2540
2541 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2542 self.columnar_selection_tail.take();
2543 if self.selections.pending_anchor().is_some() {
2544 let selections = self.selections.all::<usize>(cx);
2545 self.change_selections(None, window, cx, |s| {
2546 s.select(selections);
2547 s.clear_pending();
2548 });
2549 }
2550 }
2551
2552 fn select_columns(
2553 &mut self,
2554 tail: DisplayPoint,
2555 head: DisplayPoint,
2556 goal_column: u32,
2557 display_map: &DisplaySnapshot,
2558 window: &mut Window,
2559 cx: &mut Context<Self>,
2560 ) {
2561 let start_row = cmp::min(tail.row(), head.row());
2562 let end_row = cmp::max(tail.row(), head.row());
2563 let start_column = cmp::min(tail.column(), goal_column);
2564 let end_column = cmp::max(tail.column(), goal_column);
2565 let reversed = start_column < tail.column();
2566
2567 let selection_ranges = (start_row.0..=end_row.0)
2568 .map(DisplayRow)
2569 .filter_map(|row| {
2570 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2571 let start = display_map
2572 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2573 .to_point(display_map);
2574 let end = display_map
2575 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2576 .to_point(display_map);
2577 if reversed {
2578 Some(end..start)
2579 } else {
2580 Some(start..end)
2581 }
2582 } else {
2583 None
2584 }
2585 })
2586 .collect::<Vec<_>>();
2587
2588 self.change_selections(None, window, cx, |s| {
2589 s.select_ranges(selection_ranges);
2590 });
2591 cx.notify();
2592 }
2593
2594 pub fn has_pending_nonempty_selection(&self) -> bool {
2595 let pending_nonempty_selection = match self.selections.pending_anchor() {
2596 Some(Selection { start, end, .. }) => start != end,
2597 None => false,
2598 };
2599
2600 pending_nonempty_selection
2601 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2602 }
2603
2604 pub fn has_pending_selection(&self) -> bool {
2605 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2606 }
2607
2608 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2609 self.selection_mark_mode = false;
2610
2611 if self.clear_expanded_diff_hunks(cx) {
2612 cx.notify();
2613 return;
2614 }
2615 if self.dismiss_menus_and_popups(true, window, cx) {
2616 return;
2617 }
2618
2619 if self.mode == EditorMode::Full
2620 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2621 {
2622 return;
2623 }
2624
2625 cx.propagate();
2626 }
2627
2628 pub fn dismiss_menus_and_popups(
2629 &mut self,
2630 is_user_requested: bool,
2631 window: &mut Window,
2632 cx: &mut Context<Self>,
2633 ) -> bool {
2634 if self.take_rename(false, window, cx).is_some() {
2635 return true;
2636 }
2637
2638 if hide_hover(self, cx) {
2639 return true;
2640 }
2641
2642 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2643 return true;
2644 }
2645
2646 if self.hide_context_menu(window, cx).is_some() {
2647 return true;
2648 }
2649
2650 if self.mouse_context_menu.take().is_some() {
2651 return true;
2652 }
2653
2654 if is_user_requested && self.discard_inline_completion(true, cx) {
2655 return true;
2656 }
2657
2658 if self.snippet_stack.pop().is_some() {
2659 return true;
2660 }
2661
2662 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2663 self.dismiss_diagnostics(cx);
2664 return true;
2665 }
2666
2667 false
2668 }
2669
2670 fn linked_editing_ranges_for(
2671 &self,
2672 selection: Range<text::Anchor>,
2673 cx: &App,
2674 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2675 if self.linked_edit_ranges.is_empty() {
2676 return None;
2677 }
2678 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2679 selection.end.buffer_id.and_then(|end_buffer_id| {
2680 if selection.start.buffer_id != Some(end_buffer_id) {
2681 return None;
2682 }
2683 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2684 let snapshot = buffer.read(cx).snapshot();
2685 self.linked_edit_ranges
2686 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2687 .map(|ranges| (ranges, snapshot, buffer))
2688 })?;
2689 use text::ToOffset as TO;
2690 // find offset from the start of current range to current cursor position
2691 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2692
2693 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2694 let start_difference = start_offset - start_byte_offset;
2695 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2696 let end_difference = end_offset - start_byte_offset;
2697 // Current range has associated linked ranges.
2698 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2699 for range in linked_ranges.iter() {
2700 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2701 let end_offset = start_offset + end_difference;
2702 let start_offset = start_offset + start_difference;
2703 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2704 continue;
2705 }
2706 if self.selections.disjoint_anchor_ranges().any(|s| {
2707 if s.start.buffer_id != selection.start.buffer_id
2708 || s.end.buffer_id != selection.end.buffer_id
2709 {
2710 return false;
2711 }
2712 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2713 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2714 }) {
2715 continue;
2716 }
2717 let start = buffer_snapshot.anchor_after(start_offset);
2718 let end = buffer_snapshot.anchor_after(end_offset);
2719 linked_edits
2720 .entry(buffer.clone())
2721 .or_default()
2722 .push(start..end);
2723 }
2724 Some(linked_edits)
2725 }
2726
2727 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2728 let text: Arc<str> = text.into();
2729
2730 if self.read_only(cx) {
2731 return;
2732 }
2733
2734 let selections = self.selections.all_adjusted(cx);
2735 let mut bracket_inserted = false;
2736 let mut edits = Vec::new();
2737 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2738 let mut new_selections = Vec::with_capacity(selections.len());
2739 let mut new_autoclose_regions = Vec::new();
2740 let snapshot = self.buffer.read(cx).read(cx);
2741
2742 for (selection, autoclose_region) in
2743 self.selections_with_autoclose_regions(selections, &snapshot)
2744 {
2745 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2746 // Determine if the inserted text matches the opening or closing
2747 // bracket of any of this language's bracket pairs.
2748 let mut bracket_pair = None;
2749 let mut is_bracket_pair_start = false;
2750 let mut is_bracket_pair_end = false;
2751 if !text.is_empty() {
2752 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2753 // and they are removing the character that triggered IME popup.
2754 for (pair, enabled) in scope.brackets() {
2755 if !pair.close && !pair.surround {
2756 continue;
2757 }
2758
2759 if enabled && pair.start.ends_with(text.as_ref()) {
2760 let prefix_len = pair.start.len() - text.len();
2761 let preceding_text_matches_prefix = prefix_len == 0
2762 || (selection.start.column >= (prefix_len as u32)
2763 && snapshot.contains_str_at(
2764 Point::new(
2765 selection.start.row,
2766 selection.start.column - (prefix_len as u32),
2767 ),
2768 &pair.start[..prefix_len],
2769 ));
2770 if preceding_text_matches_prefix {
2771 bracket_pair = Some(pair.clone());
2772 is_bracket_pair_start = true;
2773 break;
2774 }
2775 }
2776 if pair.end.as_str() == text.as_ref() {
2777 bracket_pair = Some(pair.clone());
2778 is_bracket_pair_end = true;
2779 break;
2780 }
2781 }
2782 }
2783
2784 if let Some(bracket_pair) = bracket_pair {
2785 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2786 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2787 let auto_surround =
2788 self.use_auto_surround && snapshot_settings.use_auto_surround;
2789 if selection.is_empty() {
2790 if is_bracket_pair_start {
2791 // If the inserted text is a suffix of an opening bracket and the
2792 // selection is preceded by the rest of the opening bracket, then
2793 // insert the closing bracket.
2794 let following_text_allows_autoclose = snapshot
2795 .chars_at(selection.start)
2796 .next()
2797 .map_or(true, |c| scope.should_autoclose_before(c));
2798
2799 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2800 && bracket_pair.start.len() == 1
2801 {
2802 let target = bracket_pair.start.chars().next().unwrap();
2803 let current_line_count = snapshot
2804 .reversed_chars_at(selection.start)
2805 .take_while(|&c| c != '\n')
2806 .filter(|&c| c == target)
2807 .count();
2808 current_line_count % 2 == 1
2809 } else {
2810 false
2811 };
2812
2813 if autoclose
2814 && bracket_pair.close
2815 && following_text_allows_autoclose
2816 && !is_closing_quote
2817 {
2818 let anchor = snapshot.anchor_before(selection.end);
2819 new_selections.push((selection.map(|_| anchor), text.len()));
2820 new_autoclose_regions.push((
2821 anchor,
2822 text.len(),
2823 selection.id,
2824 bracket_pair.clone(),
2825 ));
2826 edits.push((
2827 selection.range(),
2828 format!("{}{}", text, bracket_pair.end).into(),
2829 ));
2830 bracket_inserted = true;
2831 continue;
2832 }
2833 }
2834
2835 if let Some(region) = autoclose_region {
2836 // If the selection is followed by an auto-inserted closing bracket,
2837 // then don't insert that closing bracket again; just move the selection
2838 // past the closing bracket.
2839 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2840 && text.as_ref() == region.pair.end.as_str();
2841 if should_skip {
2842 let anchor = snapshot.anchor_after(selection.end);
2843 new_selections
2844 .push((selection.map(|_| anchor), region.pair.end.len()));
2845 continue;
2846 }
2847 }
2848
2849 let always_treat_brackets_as_autoclosed = snapshot
2850 .settings_at(selection.start, cx)
2851 .always_treat_brackets_as_autoclosed;
2852 if always_treat_brackets_as_autoclosed
2853 && is_bracket_pair_end
2854 && snapshot.contains_str_at(selection.end, text.as_ref())
2855 {
2856 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2857 // and the inserted text is a closing bracket and the selection is followed
2858 // by the closing bracket then move the selection past the closing bracket.
2859 let anchor = snapshot.anchor_after(selection.end);
2860 new_selections.push((selection.map(|_| anchor), text.len()));
2861 continue;
2862 }
2863 }
2864 // If an opening bracket is 1 character long and is typed while
2865 // text is selected, then surround that text with the bracket pair.
2866 else if auto_surround
2867 && bracket_pair.surround
2868 && is_bracket_pair_start
2869 && bracket_pair.start.chars().count() == 1
2870 {
2871 edits.push((selection.start..selection.start, text.clone()));
2872 edits.push((
2873 selection.end..selection.end,
2874 bracket_pair.end.as_str().into(),
2875 ));
2876 bracket_inserted = true;
2877 new_selections.push((
2878 Selection {
2879 id: selection.id,
2880 start: snapshot.anchor_after(selection.start),
2881 end: snapshot.anchor_before(selection.end),
2882 reversed: selection.reversed,
2883 goal: selection.goal,
2884 },
2885 0,
2886 ));
2887 continue;
2888 }
2889 }
2890 }
2891
2892 if self.auto_replace_emoji_shortcode
2893 && selection.is_empty()
2894 && text.as_ref().ends_with(':')
2895 {
2896 if let Some(possible_emoji_short_code) =
2897 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2898 {
2899 if !possible_emoji_short_code.is_empty() {
2900 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2901 let emoji_shortcode_start = Point::new(
2902 selection.start.row,
2903 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2904 );
2905
2906 // Remove shortcode from buffer
2907 edits.push((
2908 emoji_shortcode_start..selection.start,
2909 "".to_string().into(),
2910 ));
2911 new_selections.push((
2912 Selection {
2913 id: selection.id,
2914 start: snapshot.anchor_after(emoji_shortcode_start),
2915 end: snapshot.anchor_before(selection.start),
2916 reversed: selection.reversed,
2917 goal: selection.goal,
2918 },
2919 0,
2920 ));
2921
2922 // Insert emoji
2923 let selection_start_anchor = snapshot.anchor_after(selection.start);
2924 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2925 edits.push((selection.start..selection.end, emoji.to_string().into()));
2926
2927 continue;
2928 }
2929 }
2930 }
2931 }
2932
2933 // If not handling any auto-close operation, then just replace the selected
2934 // text with the given input and move the selection to the end of the
2935 // newly inserted text.
2936 let anchor = snapshot.anchor_after(selection.end);
2937 if !self.linked_edit_ranges.is_empty() {
2938 let start_anchor = snapshot.anchor_before(selection.start);
2939
2940 let is_word_char = text.chars().next().map_or(true, |char| {
2941 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2942 classifier.is_word(char)
2943 });
2944
2945 if is_word_char {
2946 if let Some(ranges) = self
2947 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2948 {
2949 for (buffer, edits) in ranges {
2950 linked_edits
2951 .entry(buffer.clone())
2952 .or_default()
2953 .extend(edits.into_iter().map(|range| (range, text.clone())));
2954 }
2955 }
2956 }
2957 }
2958
2959 new_selections.push((selection.map(|_| anchor), 0));
2960 edits.push((selection.start..selection.end, text.clone()));
2961 }
2962
2963 drop(snapshot);
2964
2965 self.transact(window, cx, |this, window, cx| {
2966 this.buffer.update(cx, |buffer, cx| {
2967 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2968 });
2969 for (buffer, edits) in linked_edits {
2970 buffer.update(cx, |buffer, cx| {
2971 let snapshot = buffer.snapshot();
2972 let edits = edits
2973 .into_iter()
2974 .map(|(range, text)| {
2975 use text::ToPoint as TP;
2976 let end_point = TP::to_point(&range.end, &snapshot);
2977 let start_point = TP::to_point(&range.start, &snapshot);
2978 (start_point..end_point, text)
2979 })
2980 .sorted_by_key(|(range, _)| range.start)
2981 .collect::<Vec<_>>();
2982 buffer.edit(edits, None, cx);
2983 })
2984 }
2985 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2986 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2987 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2988 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2989 .zip(new_selection_deltas)
2990 .map(|(selection, delta)| Selection {
2991 id: selection.id,
2992 start: selection.start + delta,
2993 end: selection.end + delta,
2994 reversed: selection.reversed,
2995 goal: SelectionGoal::None,
2996 })
2997 .collect::<Vec<_>>();
2998
2999 let mut i = 0;
3000 for (position, delta, selection_id, pair) in new_autoclose_regions {
3001 let position = position.to_offset(&map.buffer_snapshot) + delta;
3002 let start = map.buffer_snapshot.anchor_before(position);
3003 let end = map.buffer_snapshot.anchor_after(position);
3004 while let Some(existing_state) = this.autoclose_regions.get(i) {
3005 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3006 Ordering::Less => i += 1,
3007 Ordering::Greater => break,
3008 Ordering::Equal => {
3009 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3010 Ordering::Less => i += 1,
3011 Ordering::Equal => break,
3012 Ordering::Greater => break,
3013 }
3014 }
3015 }
3016 }
3017 this.autoclose_regions.insert(
3018 i,
3019 AutocloseRegion {
3020 selection_id,
3021 range: start..end,
3022 pair,
3023 },
3024 );
3025 }
3026
3027 let had_active_inline_completion = this.has_active_inline_completion();
3028 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3029 s.select(new_selections)
3030 });
3031
3032 if !bracket_inserted {
3033 if let Some(on_type_format_task) =
3034 this.trigger_on_type_formatting(text.to_string(), window, cx)
3035 {
3036 on_type_format_task.detach_and_log_err(cx);
3037 }
3038 }
3039
3040 let editor_settings = EditorSettings::get_global(cx);
3041 if bracket_inserted
3042 && (editor_settings.auto_signature_help
3043 || editor_settings.show_signature_help_after_edits)
3044 {
3045 this.show_signature_help(&ShowSignatureHelp, window, cx);
3046 }
3047
3048 let trigger_in_words =
3049 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3050 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3051 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3052 this.refresh_inline_completion(true, false, window, cx);
3053 });
3054 }
3055
3056 fn find_possible_emoji_shortcode_at_position(
3057 snapshot: &MultiBufferSnapshot,
3058 position: Point,
3059 ) -> Option<String> {
3060 let mut chars = Vec::new();
3061 let mut found_colon = false;
3062 for char in snapshot.reversed_chars_at(position).take(100) {
3063 // Found a possible emoji shortcode in the middle of the buffer
3064 if found_colon {
3065 if char.is_whitespace() {
3066 chars.reverse();
3067 return Some(chars.iter().collect());
3068 }
3069 // If the previous character is not a whitespace, we are in the middle of a word
3070 // and we only want to complete the shortcode if the word is made up of other emojis
3071 let mut containing_word = String::new();
3072 for ch in snapshot
3073 .reversed_chars_at(position)
3074 .skip(chars.len() + 1)
3075 .take(100)
3076 {
3077 if ch.is_whitespace() {
3078 break;
3079 }
3080 containing_word.push(ch);
3081 }
3082 let containing_word = containing_word.chars().rev().collect::<String>();
3083 if util::word_consists_of_emojis(containing_word.as_str()) {
3084 chars.reverse();
3085 return Some(chars.iter().collect());
3086 }
3087 }
3088
3089 if char.is_whitespace() || !char.is_ascii() {
3090 return None;
3091 }
3092 if char == ':' {
3093 found_colon = true;
3094 } else {
3095 chars.push(char);
3096 }
3097 }
3098 // Found a possible emoji shortcode at the beginning of the buffer
3099 chars.reverse();
3100 Some(chars.iter().collect())
3101 }
3102
3103 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3104 self.transact(window, cx, |this, window, cx| {
3105 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3106 let selections = this.selections.all::<usize>(cx);
3107 let multi_buffer = this.buffer.read(cx);
3108 let buffer = multi_buffer.snapshot(cx);
3109 selections
3110 .iter()
3111 .map(|selection| {
3112 let start_point = selection.start.to_point(&buffer);
3113 let mut indent =
3114 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3115 indent.len = cmp::min(indent.len, start_point.column);
3116 let start = selection.start;
3117 let end = selection.end;
3118 let selection_is_empty = start == end;
3119 let language_scope = buffer.language_scope_at(start);
3120 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3121 &language_scope
3122 {
3123 let leading_whitespace_len = buffer
3124 .reversed_chars_at(start)
3125 .take_while(|c| c.is_whitespace() && *c != '\n')
3126 .map(|c| c.len_utf8())
3127 .sum::<usize>();
3128
3129 let trailing_whitespace_len = buffer
3130 .chars_at(end)
3131 .take_while(|c| c.is_whitespace() && *c != '\n')
3132 .map(|c| c.len_utf8())
3133 .sum::<usize>();
3134
3135 let insert_extra_newline =
3136 language.brackets().any(|(pair, enabled)| {
3137 let pair_start = pair.start.trim_end();
3138 let pair_end = pair.end.trim_start();
3139
3140 enabled
3141 && pair.newline
3142 && buffer.contains_str_at(
3143 end + trailing_whitespace_len,
3144 pair_end,
3145 )
3146 && buffer.contains_str_at(
3147 (start - leading_whitespace_len)
3148 .saturating_sub(pair_start.len()),
3149 pair_start,
3150 )
3151 });
3152
3153 // Comment extension on newline is allowed only for cursor selections
3154 let comment_delimiter = maybe!({
3155 if !selection_is_empty {
3156 return None;
3157 }
3158
3159 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3160 return None;
3161 }
3162
3163 let delimiters = language.line_comment_prefixes();
3164 let max_len_of_delimiter =
3165 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3166 let (snapshot, range) =
3167 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3168
3169 let mut index_of_first_non_whitespace = 0;
3170 let comment_candidate = snapshot
3171 .chars_for_range(range)
3172 .skip_while(|c| {
3173 let should_skip = c.is_whitespace();
3174 if should_skip {
3175 index_of_first_non_whitespace += 1;
3176 }
3177 should_skip
3178 })
3179 .take(max_len_of_delimiter)
3180 .collect::<String>();
3181 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3182 comment_candidate.starts_with(comment_prefix.as_ref())
3183 })?;
3184 let cursor_is_placed_after_comment_marker =
3185 index_of_first_non_whitespace + comment_prefix.len()
3186 <= start_point.column as usize;
3187 if cursor_is_placed_after_comment_marker {
3188 Some(comment_prefix.clone())
3189 } else {
3190 None
3191 }
3192 });
3193 (comment_delimiter, insert_extra_newline)
3194 } else {
3195 (None, false)
3196 };
3197
3198 let capacity_for_delimiter = comment_delimiter
3199 .as_deref()
3200 .map(str::len)
3201 .unwrap_or_default();
3202 let mut new_text =
3203 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3204 new_text.push('\n');
3205 new_text.extend(indent.chars());
3206 if let Some(delimiter) = &comment_delimiter {
3207 new_text.push_str(delimiter);
3208 }
3209 if insert_extra_newline {
3210 new_text = new_text.repeat(2);
3211 }
3212
3213 let anchor = buffer.anchor_after(end);
3214 let new_selection = selection.map(|_| anchor);
3215 (
3216 (start..end, new_text),
3217 (insert_extra_newline, new_selection),
3218 )
3219 })
3220 .unzip()
3221 };
3222
3223 this.edit_with_autoindent(edits, cx);
3224 let buffer = this.buffer.read(cx).snapshot(cx);
3225 let new_selections = selection_fixup_info
3226 .into_iter()
3227 .map(|(extra_newline_inserted, new_selection)| {
3228 let mut cursor = new_selection.end.to_point(&buffer);
3229 if extra_newline_inserted {
3230 cursor.row -= 1;
3231 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3232 }
3233 new_selection.map(|_| cursor)
3234 })
3235 .collect();
3236
3237 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3238 s.select(new_selections)
3239 });
3240 this.refresh_inline_completion(true, false, window, cx);
3241 });
3242 }
3243
3244 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3245 let buffer = self.buffer.read(cx);
3246 let snapshot = buffer.snapshot(cx);
3247
3248 let mut edits = Vec::new();
3249 let mut rows = Vec::new();
3250
3251 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3252 let cursor = selection.head();
3253 let row = cursor.row;
3254
3255 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3256
3257 let newline = "\n".to_string();
3258 edits.push((start_of_line..start_of_line, newline));
3259
3260 rows.push(row + rows_inserted as u32);
3261 }
3262
3263 self.transact(window, cx, |editor, window, cx| {
3264 editor.edit(edits, cx);
3265
3266 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3267 let mut index = 0;
3268 s.move_cursors_with(|map, _, _| {
3269 let row = rows[index];
3270 index += 1;
3271
3272 let point = Point::new(row, 0);
3273 let boundary = map.next_line_boundary(point).1;
3274 let clipped = map.clip_point(boundary, Bias::Left);
3275
3276 (clipped, SelectionGoal::None)
3277 });
3278 });
3279
3280 let mut indent_edits = Vec::new();
3281 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3282 for row in rows {
3283 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3284 for (row, indent) in indents {
3285 if indent.len == 0 {
3286 continue;
3287 }
3288
3289 let text = match indent.kind {
3290 IndentKind::Space => " ".repeat(indent.len as usize),
3291 IndentKind::Tab => "\t".repeat(indent.len as usize),
3292 };
3293 let point = Point::new(row.0, 0);
3294 indent_edits.push((point..point, text));
3295 }
3296 }
3297 editor.edit(indent_edits, cx);
3298 });
3299 }
3300
3301 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3302 let buffer = self.buffer.read(cx);
3303 let snapshot = buffer.snapshot(cx);
3304
3305 let mut edits = Vec::new();
3306 let mut rows = Vec::new();
3307 let mut rows_inserted = 0;
3308
3309 for selection in self.selections.all_adjusted(cx) {
3310 let cursor = selection.head();
3311 let row = cursor.row;
3312
3313 let point = Point::new(row + 1, 0);
3314 let start_of_line = snapshot.clip_point(point, Bias::Left);
3315
3316 let newline = "\n".to_string();
3317 edits.push((start_of_line..start_of_line, newline));
3318
3319 rows_inserted += 1;
3320 rows.push(row + rows_inserted);
3321 }
3322
3323 self.transact(window, cx, |editor, window, cx| {
3324 editor.edit(edits, cx);
3325
3326 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3327 let mut index = 0;
3328 s.move_cursors_with(|map, _, _| {
3329 let row = rows[index];
3330 index += 1;
3331
3332 let point = Point::new(row, 0);
3333 let boundary = map.next_line_boundary(point).1;
3334 let clipped = map.clip_point(boundary, Bias::Left);
3335
3336 (clipped, SelectionGoal::None)
3337 });
3338 });
3339
3340 let mut indent_edits = Vec::new();
3341 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3342 for row in rows {
3343 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3344 for (row, indent) in indents {
3345 if indent.len == 0 {
3346 continue;
3347 }
3348
3349 let text = match indent.kind {
3350 IndentKind::Space => " ".repeat(indent.len as usize),
3351 IndentKind::Tab => "\t".repeat(indent.len as usize),
3352 };
3353 let point = Point::new(row.0, 0);
3354 indent_edits.push((point..point, text));
3355 }
3356 }
3357 editor.edit(indent_edits, cx);
3358 });
3359 }
3360
3361 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3362 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3363 original_indent_columns: Vec::new(),
3364 });
3365 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3366 }
3367
3368 fn insert_with_autoindent_mode(
3369 &mut self,
3370 text: &str,
3371 autoindent_mode: Option<AutoindentMode>,
3372 window: &mut Window,
3373 cx: &mut Context<Self>,
3374 ) {
3375 if self.read_only(cx) {
3376 return;
3377 }
3378
3379 let text: Arc<str> = text.into();
3380 self.transact(window, cx, |this, window, cx| {
3381 let old_selections = this.selections.all_adjusted(cx);
3382 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3383 let anchors = {
3384 let snapshot = buffer.read(cx);
3385 old_selections
3386 .iter()
3387 .map(|s| {
3388 let anchor = snapshot.anchor_after(s.head());
3389 s.map(|_| anchor)
3390 })
3391 .collect::<Vec<_>>()
3392 };
3393 buffer.edit(
3394 old_selections
3395 .iter()
3396 .map(|s| (s.start..s.end, text.clone())),
3397 autoindent_mode,
3398 cx,
3399 );
3400 anchors
3401 });
3402
3403 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3404 s.select_anchors(selection_anchors);
3405 });
3406
3407 cx.notify();
3408 });
3409 }
3410
3411 fn trigger_completion_on_input(
3412 &mut self,
3413 text: &str,
3414 trigger_in_words: bool,
3415 window: &mut Window,
3416 cx: &mut Context<Self>,
3417 ) {
3418 if self.is_completion_trigger(text, trigger_in_words, cx) {
3419 self.show_completions(
3420 &ShowCompletions {
3421 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3422 },
3423 window,
3424 cx,
3425 );
3426 } else {
3427 self.hide_context_menu(window, cx);
3428 }
3429 }
3430
3431 fn is_completion_trigger(
3432 &self,
3433 text: &str,
3434 trigger_in_words: bool,
3435 cx: &mut Context<Self>,
3436 ) -> bool {
3437 let position = self.selections.newest_anchor().head();
3438 let multibuffer = self.buffer.read(cx);
3439 let Some(buffer) = position
3440 .buffer_id
3441 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3442 else {
3443 return false;
3444 };
3445
3446 if let Some(completion_provider) = &self.completion_provider {
3447 completion_provider.is_completion_trigger(
3448 &buffer,
3449 position.text_anchor,
3450 text,
3451 trigger_in_words,
3452 cx,
3453 )
3454 } else {
3455 false
3456 }
3457 }
3458
3459 /// If any empty selections is touching the start of its innermost containing autoclose
3460 /// region, expand it to select the brackets.
3461 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3462 let selections = self.selections.all::<usize>(cx);
3463 let buffer = self.buffer.read(cx).read(cx);
3464 let new_selections = self
3465 .selections_with_autoclose_regions(selections, &buffer)
3466 .map(|(mut selection, region)| {
3467 if !selection.is_empty() {
3468 return selection;
3469 }
3470
3471 if let Some(region) = region {
3472 let mut range = region.range.to_offset(&buffer);
3473 if selection.start == range.start && range.start >= region.pair.start.len() {
3474 range.start -= region.pair.start.len();
3475 if buffer.contains_str_at(range.start, ®ion.pair.start)
3476 && buffer.contains_str_at(range.end, ®ion.pair.end)
3477 {
3478 range.end += region.pair.end.len();
3479 selection.start = range.start;
3480 selection.end = range.end;
3481
3482 return selection;
3483 }
3484 }
3485 }
3486
3487 let always_treat_brackets_as_autoclosed = buffer
3488 .settings_at(selection.start, cx)
3489 .always_treat_brackets_as_autoclosed;
3490
3491 if !always_treat_brackets_as_autoclosed {
3492 return selection;
3493 }
3494
3495 if let Some(scope) = buffer.language_scope_at(selection.start) {
3496 for (pair, enabled) in scope.brackets() {
3497 if !enabled || !pair.close {
3498 continue;
3499 }
3500
3501 if buffer.contains_str_at(selection.start, &pair.end) {
3502 let pair_start_len = pair.start.len();
3503 if buffer.contains_str_at(
3504 selection.start.saturating_sub(pair_start_len),
3505 &pair.start,
3506 ) {
3507 selection.start -= pair_start_len;
3508 selection.end += pair.end.len();
3509
3510 return selection;
3511 }
3512 }
3513 }
3514 }
3515
3516 selection
3517 })
3518 .collect();
3519
3520 drop(buffer);
3521 self.change_selections(None, window, cx, |selections| {
3522 selections.select(new_selections)
3523 });
3524 }
3525
3526 /// Iterate the given selections, and for each one, find the smallest surrounding
3527 /// autoclose region. This uses the ordering of the selections and the autoclose
3528 /// regions to avoid repeated comparisons.
3529 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3530 &'a self,
3531 selections: impl IntoIterator<Item = Selection<D>>,
3532 buffer: &'a MultiBufferSnapshot,
3533 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3534 let mut i = 0;
3535 let mut regions = self.autoclose_regions.as_slice();
3536 selections.into_iter().map(move |selection| {
3537 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3538
3539 let mut enclosing = None;
3540 while let Some(pair_state) = regions.get(i) {
3541 if pair_state.range.end.to_offset(buffer) < range.start {
3542 regions = ®ions[i + 1..];
3543 i = 0;
3544 } else if pair_state.range.start.to_offset(buffer) > range.end {
3545 break;
3546 } else {
3547 if pair_state.selection_id == selection.id {
3548 enclosing = Some(pair_state);
3549 }
3550 i += 1;
3551 }
3552 }
3553
3554 (selection, enclosing)
3555 })
3556 }
3557
3558 /// Remove any autoclose regions that no longer contain their selection.
3559 fn invalidate_autoclose_regions(
3560 &mut self,
3561 mut selections: &[Selection<Anchor>],
3562 buffer: &MultiBufferSnapshot,
3563 ) {
3564 self.autoclose_regions.retain(|state| {
3565 let mut i = 0;
3566 while let Some(selection) = selections.get(i) {
3567 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3568 selections = &selections[1..];
3569 continue;
3570 }
3571 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3572 break;
3573 }
3574 if selection.id == state.selection_id {
3575 return true;
3576 } else {
3577 i += 1;
3578 }
3579 }
3580 false
3581 });
3582 }
3583
3584 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3585 let offset = position.to_offset(buffer);
3586 let (word_range, kind) = buffer.surrounding_word(offset, true);
3587 if offset > word_range.start && kind == Some(CharKind::Word) {
3588 Some(
3589 buffer
3590 .text_for_range(word_range.start..offset)
3591 .collect::<String>(),
3592 )
3593 } else {
3594 None
3595 }
3596 }
3597
3598 pub fn toggle_inlay_hints(
3599 &mut self,
3600 _: &ToggleInlayHints,
3601 _: &mut Window,
3602 cx: &mut Context<Self>,
3603 ) {
3604 self.refresh_inlay_hints(
3605 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3606 cx,
3607 );
3608 }
3609
3610 pub fn inlay_hints_enabled(&self) -> bool {
3611 self.inlay_hint_cache.enabled
3612 }
3613
3614 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3615 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3616 return;
3617 }
3618
3619 let reason_description = reason.description();
3620 let ignore_debounce = matches!(
3621 reason,
3622 InlayHintRefreshReason::SettingsChange(_)
3623 | InlayHintRefreshReason::Toggle(_)
3624 | InlayHintRefreshReason::ExcerptsRemoved(_)
3625 );
3626 let (invalidate_cache, required_languages) = match reason {
3627 InlayHintRefreshReason::Toggle(enabled) => {
3628 self.inlay_hint_cache.enabled = enabled;
3629 if enabled {
3630 (InvalidationStrategy::RefreshRequested, None)
3631 } else {
3632 self.inlay_hint_cache.clear();
3633 self.splice_inlays(
3634 &self
3635 .visible_inlay_hints(cx)
3636 .iter()
3637 .map(|inlay| inlay.id)
3638 .collect::<Vec<InlayId>>(),
3639 Vec::new(),
3640 cx,
3641 );
3642 return;
3643 }
3644 }
3645 InlayHintRefreshReason::SettingsChange(new_settings) => {
3646 match self.inlay_hint_cache.update_settings(
3647 &self.buffer,
3648 new_settings,
3649 self.visible_inlay_hints(cx),
3650 cx,
3651 ) {
3652 ControlFlow::Break(Some(InlaySplice {
3653 to_remove,
3654 to_insert,
3655 })) => {
3656 self.splice_inlays(&to_remove, to_insert, cx);
3657 return;
3658 }
3659 ControlFlow::Break(None) => return,
3660 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3661 }
3662 }
3663 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3664 if let Some(InlaySplice {
3665 to_remove,
3666 to_insert,
3667 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3668 {
3669 self.splice_inlays(&to_remove, to_insert, cx);
3670 }
3671 return;
3672 }
3673 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3674 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3675 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3676 }
3677 InlayHintRefreshReason::RefreshRequested => {
3678 (InvalidationStrategy::RefreshRequested, None)
3679 }
3680 };
3681
3682 if let Some(InlaySplice {
3683 to_remove,
3684 to_insert,
3685 }) = self.inlay_hint_cache.spawn_hint_refresh(
3686 reason_description,
3687 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3688 invalidate_cache,
3689 ignore_debounce,
3690 cx,
3691 ) {
3692 self.splice_inlays(&to_remove, to_insert, cx);
3693 }
3694 }
3695
3696 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3697 self.display_map
3698 .read(cx)
3699 .current_inlays()
3700 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3701 .cloned()
3702 .collect()
3703 }
3704
3705 pub fn excerpts_for_inlay_hints_query(
3706 &self,
3707 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3708 cx: &mut Context<Editor>,
3709 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3710 let Some(project) = self.project.as_ref() else {
3711 return HashMap::default();
3712 };
3713 let project = project.read(cx);
3714 let multi_buffer = self.buffer().read(cx);
3715 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3716 let multi_buffer_visible_start = self
3717 .scroll_manager
3718 .anchor()
3719 .anchor
3720 .to_point(&multi_buffer_snapshot);
3721 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3722 multi_buffer_visible_start
3723 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3724 Bias::Left,
3725 );
3726 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3727 multi_buffer_snapshot
3728 .range_to_buffer_ranges(multi_buffer_visible_range)
3729 .into_iter()
3730 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3731 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3732 let buffer_file = project::File::from_dyn(buffer.file())?;
3733 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3734 let worktree_entry = buffer_worktree
3735 .read(cx)
3736 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3737 if worktree_entry.is_ignored {
3738 return None;
3739 }
3740
3741 let language = buffer.language()?;
3742 if let Some(restrict_to_languages) = restrict_to_languages {
3743 if !restrict_to_languages.contains(language) {
3744 return None;
3745 }
3746 }
3747 Some((
3748 excerpt_id,
3749 (
3750 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3751 buffer.version().clone(),
3752 excerpt_visible_range,
3753 ),
3754 ))
3755 })
3756 .collect()
3757 }
3758
3759 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3760 TextLayoutDetails {
3761 text_system: window.text_system().clone(),
3762 editor_style: self.style.clone().unwrap(),
3763 rem_size: window.rem_size(),
3764 scroll_anchor: self.scroll_manager.anchor(),
3765 visible_rows: self.visible_line_count(),
3766 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3767 }
3768 }
3769
3770 pub fn splice_inlays(
3771 &self,
3772 to_remove: &[InlayId],
3773 to_insert: Vec<Inlay>,
3774 cx: &mut Context<Self>,
3775 ) {
3776 self.display_map.update(cx, |display_map, cx| {
3777 display_map.splice_inlays(to_remove, to_insert, cx)
3778 });
3779 cx.notify();
3780 }
3781
3782 fn trigger_on_type_formatting(
3783 &self,
3784 input: String,
3785 window: &mut Window,
3786 cx: &mut Context<Self>,
3787 ) -> Option<Task<Result<()>>> {
3788 if input.len() != 1 {
3789 return None;
3790 }
3791
3792 let project = self.project.as_ref()?;
3793 let position = self.selections.newest_anchor().head();
3794 let (buffer, buffer_position) = self
3795 .buffer
3796 .read(cx)
3797 .text_anchor_for_position(position, cx)?;
3798
3799 let settings = language_settings::language_settings(
3800 buffer
3801 .read(cx)
3802 .language_at(buffer_position)
3803 .map(|l| l.name()),
3804 buffer.read(cx).file(),
3805 cx,
3806 );
3807 if !settings.use_on_type_format {
3808 return None;
3809 }
3810
3811 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3812 // hence we do LSP request & edit on host side only — add formats to host's history.
3813 let push_to_lsp_host_history = true;
3814 // If this is not the host, append its history with new edits.
3815 let push_to_client_history = project.read(cx).is_via_collab();
3816
3817 let on_type_formatting = project.update(cx, |project, cx| {
3818 project.on_type_format(
3819 buffer.clone(),
3820 buffer_position,
3821 input,
3822 push_to_lsp_host_history,
3823 cx,
3824 )
3825 });
3826 Some(cx.spawn_in(window, |editor, mut cx| async move {
3827 if let Some(transaction) = on_type_formatting.await? {
3828 if push_to_client_history {
3829 buffer
3830 .update(&mut cx, |buffer, _| {
3831 buffer.push_transaction(transaction, Instant::now());
3832 })
3833 .ok();
3834 }
3835 editor.update(&mut cx, |editor, cx| {
3836 editor.refresh_document_highlights(cx);
3837 })?;
3838 }
3839 Ok(())
3840 }))
3841 }
3842
3843 pub fn show_completions(
3844 &mut self,
3845 options: &ShowCompletions,
3846 window: &mut Window,
3847 cx: &mut Context<Self>,
3848 ) {
3849 if self.pending_rename.is_some() {
3850 return;
3851 }
3852
3853 let Some(provider) = self.completion_provider.as_ref() else {
3854 return;
3855 };
3856
3857 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3858 return;
3859 }
3860
3861 let position = self.selections.newest_anchor().head();
3862 if position.diff_base_anchor.is_some() {
3863 return;
3864 }
3865 let (buffer, buffer_position) =
3866 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3867 output
3868 } else {
3869 return;
3870 };
3871 let show_completion_documentation = buffer
3872 .read(cx)
3873 .snapshot()
3874 .settings_at(buffer_position, cx)
3875 .show_completion_documentation;
3876
3877 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3878
3879 let trigger_kind = match &options.trigger {
3880 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3881 CompletionTriggerKind::TRIGGER_CHARACTER
3882 }
3883 _ => CompletionTriggerKind::INVOKED,
3884 };
3885 let completion_context = CompletionContext {
3886 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3887 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3888 Some(String::from(trigger))
3889 } else {
3890 None
3891 }
3892 }),
3893 trigger_kind,
3894 };
3895 let completions =
3896 provider.completions(&buffer, buffer_position, completion_context, window, cx);
3897 let sort_completions = provider.sort_completions();
3898
3899 let id = post_inc(&mut self.next_completion_id);
3900 let task = cx.spawn_in(window, |editor, mut cx| {
3901 async move {
3902 editor.update(&mut cx, |this, _| {
3903 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3904 })?;
3905 let completions = completions.await.log_err();
3906 let menu = if let Some(completions) = completions {
3907 let mut menu = CompletionsMenu::new(
3908 id,
3909 sort_completions,
3910 show_completion_documentation,
3911 position,
3912 buffer.clone(),
3913 completions.into(),
3914 );
3915
3916 menu.filter(query.as_deref(), cx.background_executor().clone())
3917 .await;
3918
3919 menu.visible().then_some(menu)
3920 } else {
3921 None
3922 };
3923
3924 editor.update_in(&mut cx, |editor, window, cx| {
3925 match editor.context_menu.borrow().as_ref() {
3926 None => {}
3927 Some(CodeContextMenu::Completions(prev_menu)) => {
3928 if prev_menu.id > id {
3929 return;
3930 }
3931 }
3932 _ => return,
3933 }
3934
3935 if editor.focus_handle.is_focused(window) && menu.is_some() {
3936 let mut menu = menu.unwrap();
3937 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3938
3939 *editor.context_menu.borrow_mut() =
3940 Some(CodeContextMenu::Completions(menu));
3941
3942 if editor.show_edit_predictions_in_menu() {
3943 editor.update_visible_inline_completion(window, cx);
3944 } else {
3945 editor.discard_inline_completion(false, cx);
3946 }
3947
3948 cx.notify();
3949 } else if editor.completion_tasks.len() <= 1 {
3950 // If there are no more completion tasks and the last menu was
3951 // empty, we should hide it.
3952 let was_hidden = editor.hide_context_menu(window, cx).is_none();
3953 // If it was already hidden and we don't show inline
3954 // completions in the menu, we should also show the
3955 // inline-completion when available.
3956 if was_hidden && editor.show_edit_predictions_in_menu() {
3957 editor.update_visible_inline_completion(window, cx);
3958 }
3959 }
3960 })?;
3961
3962 Ok::<_, anyhow::Error>(())
3963 }
3964 .log_err()
3965 });
3966
3967 self.completion_tasks.push((id, task));
3968 }
3969
3970 pub fn confirm_completion(
3971 &mut self,
3972 action: &ConfirmCompletion,
3973 window: &mut Window,
3974 cx: &mut Context<Self>,
3975 ) -> Option<Task<Result<()>>> {
3976 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
3977 }
3978
3979 pub fn compose_completion(
3980 &mut self,
3981 action: &ComposeCompletion,
3982 window: &mut Window,
3983 cx: &mut Context<Self>,
3984 ) -> Option<Task<Result<()>>> {
3985 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
3986 }
3987
3988 fn do_completion(
3989 &mut self,
3990 item_ix: Option<usize>,
3991 intent: CompletionIntent,
3992 window: &mut Window,
3993 cx: &mut Context<Editor>,
3994 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3995 use language::ToOffset as _;
3996
3997 let completions_menu =
3998 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
3999 menu
4000 } else {
4001 return None;
4002 };
4003
4004 let entries = completions_menu.entries.borrow();
4005 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4006 if self.show_edit_predictions_in_menu() {
4007 self.discard_inline_completion(true, cx);
4008 }
4009 let candidate_id = mat.candidate_id;
4010 drop(entries);
4011
4012 let buffer_handle = completions_menu.buffer;
4013 let completion = completions_menu
4014 .completions
4015 .borrow()
4016 .get(candidate_id)?
4017 .clone();
4018 cx.stop_propagation();
4019
4020 let snippet;
4021 let text;
4022
4023 if completion.is_snippet() {
4024 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4025 text = snippet.as_ref().unwrap().text.clone();
4026 } else {
4027 snippet = None;
4028 text = completion.new_text.clone();
4029 };
4030 let selections = self.selections.all::<usize>(cx);
4031 let buffer = buffer_handle.read(cx);
4032 let old_range = completion.old_range.to_offset(buffer);
4033 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4034
4035 let newest_selection = self.selections.newest_anchor();
4036 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4037 return None;
4038 }
4039
4040 let lookbehind = newest_selection
4041 .start
4042 .text_anchor
4043 .to_offset(buffer)
4044 .saturating_sub(old_range.start);
4045 let lookahead = old_range
4046 .end
4047 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4048 let mut common_prefix_len = old_text
4049 .bytes()
4050 .zip(text.bytes())
4051 .take_while(|(a, b)| a == b)
4052 .count();
4053
4054 let snapshot = self.buffer.read(cx).snapshot(cx);
4055 let mut range_to_replace: Option<Range<isize>> = None;
4056 let mut ranges = Vec::new();
4057 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4058 for selection in &selections {
4059 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4060 let start = selection.start.saturating_sub(lookbehind);
4061 let end = selection.end + lookahead;
4062 if selection.id == newest_selection.id {
4063 range_to_replace = Some(
4064 ((start + common_prefix_len) as isize - selection.start as isize)
4065 ..(end as isize - selection.start as isize),
4066 );
4067 }
4068 ranges.push(start + common_prefix_len..end);
4069 } else {
4070 common_prefix_len = 0;
4071 ranges.clear();
4072 ranges.extend(selections.iter().map(|s| {
4073 if s.id == newest_selection.id {
4074 range_to_replace = Some(
4075 old_range.start.to_offset_utf16(&snapshot).0 as isize
4076 - selection.start as isize
4077 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4078 - selection.start as isize,
4079 );
4080 old_range.clone()
4081 } else {
4082 s.start..s.end
4083 }
4084 }));
4085 break;
4086 }
4087 if !self.linked_edit_ranges.is_empty() {
4088 let start_anchor = snapshot.anchor_before(selection.head());
4089 let end_anchor = snapshot.anchor_after(selection.tail());
4090 if let Some(ranges) = self
4091 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4092 {
4093 for (buffer, edits) in ranges {
4094 linked_edits.entry(buffer.clone()).or_default().extend(
4095 edits
4096 .into_iter()
4097 .map(|range| (range, text[common_prefix_len..].to_owned())),
4098 );
4099 }
4100 }
4101 }
4102 }
4103 let text = &text[common_prefix_len..];
4104
4105 cx.emit(EditorEvent::InputHandled {
4106 utf16_range_to_replace: range_to_replace,
4107 text: text.into(),
4108 });
4109
4110 self.transact(window, cx, |this, window, cx| {
4111 if let Some(mut snippet) = snippet {
4112 snippet.text = text.to_string();
4113 for tabstop in snippet
4114 .tabstops
4115 .iter_mut()
4116 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4117 {
4118 tabstop.start -= common_prefix_len as isize;
4119 tabstop.end -= common_prefix_len as isize;
4120 }
4121
4122 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4123 } else {
4124 this.buffer.update(cx, |buffer, cx| {
4125 buffer.edit(
4126 ranges.iter().map(|range| (range.clone(), text)),
4127 this.autoindent_mode.clone(),
4128 cx,
4129 );
4130 });
4131 }
4132 for (buffer, edits) in linked_edits {
4133 buffer.update(cx, |buffer, cx| {
4134 let snapshot = buffer.snapshot();
4135 let edits = edits
4136 .into_iter()
4137 .map(|(range, text)| {
4138 use text::ToPoint as TP;
4139 let end_point = TP::to_point(&range.end, &snapshot);
4140 let start_point = TP::to_point(&range.start, &snapshot);
4141 (start_point..end_point, text)
4142 })
4143 .sorted_by_key(|(range, _)| range.start)
4144 .collect::<Vec<_>>();
4145 buffer.edit(edits, None, cx);
4146 })
4147 }
4148
4149 this.refresh_inline_completion(true, false, window, cx);
4150 });
4151
4152 let show_new_completions_on_confirm = completion
4153 .confirm
4154 .as_ref()
4155 .map_or(false, |confirm| confirm(intent, window, cx));
4156 if show_new_completions_on_confirm {
4157 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4158 }
4159
4160 let provider = self.completion_provider.as_ref()?;
4161 drop(completion);
4162 let apply_edits = provider.apply_additional_edits_for_completion(
4163 buffer_handle,
4164 completions_menu.completions.clone(),
4165 candidate_id,
4166 true,
4167 cx,
4168 );
4169
4170 let editor_settings = EditorSettings::get_global(cx);
4171 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4172 // After the code completion is finished, users often want to know what signatures are needed.
4173 // so we should automatically call signature_help
4174 self.show_signature_help(&ShowSignatureHelp, window, cx);
4175 }
4176
4177 Some(cx.foreground_executor().spawn(async move {
4178 apply_edits.await?;
4179 Ok(())
4180 }))
4181 }
4182
4183 pub fn toggle_code_actions(
4184 &mut self,
4185 action: &ToggleCodeActions,
4186 window: &mut Window,
4187 cx: &mut Context<Self>,
4188 ) {
4189 let mut context_menu = self.context_menu.borrow_mut();
4190 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4191 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4192 // Toggle if we're selecting the same one
4193 *context_menu = None;
4194 cx.notify();
4195 return;
4196 } else {
4197 // Otherwise, clear it and start a new one
4198 *context_menu = None;
4199 cx.notify();
4200 }
4201 }
4202 drop(context_menu);
4203 let snapshot = self.snapshot(window, cx);
4204 let deployed_from_indicator = action.deployed_from_indicator;
4205 let mut task = self.code_actions_task.take();
4206 let action = action.clone();
4207 cx.spawn_in(window, |editor, mut cx| async move {
4208 while let Some(prev_task) = task {
4209 prev_task.await.log_err();
4210 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4211 }
4212
4213 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4214 if editor.focus_handle.is_focused(window) {
4215 let multibuffer_point = action
4216 .deployed_from_indicator
4217 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4218 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4219 let (buffer, buffer_row) = snapshot
4220 .buffer_snapshot
4221 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4222 .and_then(|(buffer_snapshot, range)| {
4223 editor
4224 .buffer
4225 .read(cx)
4226 .buffer(buffer_snapshot.remote_id())
4227 .map(|buffer| (buffer, range.start.row))
4228 })?;
4229 let (_, code_actions) = editor
4230 .available_code_actions
4231 .clone()
4232 .and_then(|(location, code_actions)| {
4233 let snapshot = location.buffer.read(cx).snapshot();
4234 let point_range = location.range.to_point(&snapshot);
4235 let point_range = point_range.start.row..=point_range.end.row;
4236 if point_range.contains(&buffer_row) {
4237 Some((location, code_actions))
4238 } else {
4239 None
4240 }
4241 })
4242 .unzip();
4243 let buffer_id = buffer.read(cx).remote_id();
4244 let tasks = editor
4245 .tasks
4246 .get(&(buffer_id, buffer_row))
4247 .map(|t| Arc::new(t.to_owned()));
4248 if tasks.is_none() && code_actions.is_none() {
4249 return None;
4250 }
4251
4252 editor.completion_tasks.clear();
4253 editor.discard_inline_completion(false, cx);
4254 let task_context =
4255 tasks
4256 .as_ref()
4257 .zip(editor.project.clone())
4258 .map(|(tasks, project)| {
4259 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4260 });
4261
4262 Some(cx.spawn_in(window, |editor, mut cx| async move {
4263 let task_context = match task_context {
4264 Some(task_context) => task_context.await,
4265 None => None,
4266 };
4267 let resolved_tasks =
4268 tasks.zip(task_context).map(|(tasks, task_context)| {
4269 Rc::new(ResolvedTasks {
4270 templates: tasks.resolve(&task_context).collect(),
4271 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4272 multibuffer_point.row,
4273 tasks.column,
4274 )),
4275 })
4276 });
4277 let spawn_straight_away = resolved_tasks
4278 .as_ref()
4279 .map_or(false, |tasks| tasks.templates.len() == 1)
4280 && code_actions
4281 .as_ref()
4282 .map_or(true, |actions| actions.is_empty());
4283 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4284 *editor.context_menu.borrow_mut() =
4285 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4286 buffer,
4287 actions: CodeActionContents {
4288 tasks: resolved_tasks,
4289 actions: code_actions,
4290 },
4291 selected_item: Default::default(),
4292 scroll_handle: UniformListScrollHandle::default(),
4293 deployed_from_indicator,
4294 }));
4295 if spawn_straight_away {
4296 if let Some(task) = editor.confirm_code_action(
4297 &ConfirmCodeAction { item_ix: Some(0) },
4298 window,
4299 cx,
4300 ) {
4301 cx.notify();
4302 return task;
4303 }
4304 }
4305 cx.notify();
4306 Task::ready(Ok(()))
4307 }) {
4308 task.await
4309 } else {
4310 Ok(())
4311 }
4312 }))
4313 } else {
4314 Some(Task::ready(Ok(())))
4315 }
4316 })?;
4317 if let Some(task) = spawned_test_task {
4318 task.await?;
4319 }
4320
4321 Ok::<_, anyhow::Error>(())
4322 })
4323 .detach_and_log_err(cx);
4324 }
4325
4326 pub fn confirm_code_action(
4327 &mut self,
4328 action: &ConfirmCodeAction,
4329 window: &mut Window,
4330 cx: &mut Context<Self>,
4331 ) -> Option<Task<Result<()>>> {
4332 let actions_menu =
4333 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4334 menu
4335 } else {
4336 return None;
4337 };
4338 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4339 let action = actions_menu.actions.get(action_ix)?;
4340 let title = action.label();
4341 let buffer = actions_menu.buffer;
4342 let workspace = self.workspace()?;
4343
4344 match action {
4345 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4346 workspace.update(cx, |workspace, cx| {
4347 workspace::tasks::schedule_resolved_task(
4348 workspace,
4349 task_source_kind,
4350 resolved_task,
4351 false,
4352 cx,
4353 );
4354
4355 Some(Task::ready(Ok(())))
4356 })
4357 }
4358 CodeActionsItem::CodeAction {
4359 excerpt_id,
4360 action,
4361 provider,
4362 } => {
4363 let apply_code_action =
4364 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4365 let workspace = workspace.downgrade();
4366 Some(cx.spawn_in(window, |editor, cx| async move {
4367 let project_transaction = apply_code_action.await?;
4368 Self::open_project_transaction(
4369 &editor,
4370 workspace,
4371 project_transaction,
4372 title,
4373 cx,
4374 )
4375 .await
4376 }))
4377 }
4378 }
4379 }
4380
4381 pub async fn open_project_transaction(
4382 this: &WeakEntity<Editor>,
4383 workspace: WeakEntity<Workspace>,
4384 transaction: ProjectTransaction,
4385 title: String,
4386 mut cx: AsyncWindowContext,
4387 ) -> Result<()> {
4388 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4389 cx.update(|_, cx| {
4390 entries.sort_unstable_by_key(|(buffer, _)| {
4391 buffer.read(cx).file().map(|f| f.path().clone())
4392 });
4393 })?;
4394
4395 // If the project transaction's edits are all contained within this editor, then
4396 // avoid opening a new editor to display them.
4397
4398 if let Some((buffer, transaction)) = entries.first() {
4399 if entries.len() == 1 {
4400 let excerpt = this.update(&mut cx, |editor, cx| {
4401 editor
4402 .buffer()
4403 .read(cx)
4404 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4405 })?;
4406 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4407 if excerpted_buffer == *buffer {
4408 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4409 let excerpt_range = excerpt_range.to_offset(buffer);
4410 buffer
4411 .edited_ranges_for_transaction::<usize>(transaction)
4412 .all(|range| {
4413 excerpt_range.start <= range.start
4414 && excerpt_range.end >= range.end
4415 })
4416 })?;
4417
4418 if all_edits_within_excerpt {
4419 return Ok(());
4420 }
4421 }
4422 }
4423 }
4424 } else {
4425 return Ok(());
4426 }
4427
4428 let mut ranges_to_highlight = Vec::new();
4429 let excerpt_buffer = cx.new(|cx| {
4430 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4431 for (buffer_handle, transaction) in &entries {
4432 let buffer = buffer_handle.read(cx);
4433 ranges_to_highlight.extend(
4434 multibuffer.push_excerpts_with_context_lines(
4435 buffer_handle.clone(),
4436 buffer
4437 .edited_ranges_for_transaction::<usize>(transaction)
4438 .collect(),
4439 DEFAULT_MULTIBUFFER_CONTEXT,
4440 cx,
4441 ),
4442 );
4443 }
4444 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4445 multibuffer
4446 })?;
4447
4448 workspace.update_in(&mut cx, |workspace, window, cx| {
4449 let project = workspace.project().clone();
4450 let editor = cx
4451 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4452 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4453 editor.update(cx, |editor, cx| {
4454 editor.highlight_background::<Self>(
4455 &ranges_to_highlight,
4456 |theme| theme.editor_highlighted_line_background,
4457 cx,
4458 );
4459 });
4460 })?;
4461
4462 Ok(())
4463 }
4464
4465 pub fn clear_code_action_providers(&mut self) {
4466 self.code_action_providers.clear();
4467 self.available_code_actions.take();
4468 }
4469
4470 pub fn add_code_action_provider(
4471 &mut self,
4472 provider: Rc<dyn CodeActionProvider>,
4473 window: &mut Window,
4474 cx: &mut Context<Self>,
4475 ) {
4476 if self
4477 .code_action_providers
4478 .iter()
4479 .any(|existing_provider| existing_provider.id() == provider.id())
4480 {
4481 return;
4482 }
4483
4484 self.code_action_providers.push(provider);
4485 self.refresh_code_actions(window, cx);
4486 }
4487
4488 pub fn remove_code_action_provider(
4489 &mut self,
4490 id: Arc<str>,
4491 window: &mut Window,
4492 cx: &mut Context<Self>,
4493 ) {
4494 self.code_action_providers
4495 .retain(|provider| provider.id() != id);
4496 self.refresh_code_actions(window, cx);
4497 }
4498
4499 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4500 let buffer = self.buffer.read(cx);
4501 let newest_selection = self.selections.newest_anchor().clone();
4502 if newest_selection.head().diff_base_anchor.is_some() {
4503 return None;
4504 }
4505 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4506 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4507 if start_buffer != end_buffer {
4508 return None;
4509 }
4510
4511 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4512 cx.background_executor()
4513 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4514 .await;
4515
4516 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4517 let providers = this.code_action_providers.clone();
4518 let tasks = this
4519 .code_action_providers
4520 .iter()
4521 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4522 .collect::<Vec<_>>();
4523 (providers, tasks)
4524 })?;
4525
4526 let mut actions = Vec::new();
4527 for (provider, provider_actions) in
4528 providers.into_iter().zip(future::join_all(tasks).await)
4529 {
4530 if let Some(provider_actions) = provider_actions.log_err() {
4531 actions.extend(provider_actions.into_iter().map(|action| {
4532 AvailableCodeAction {
4533 excerpt_id: newest_selection.start.excerpt_id,
4534 action,
4535 provider: provider.clone(),
4536 }
4537 }));
4538 }
4539 }
4540
4541 this.update(&mut cx, |this, cx| {
4542 this.available_code_actions = if actions.is_empty() {
4543 None
4544 } else {
4545 Some((
4546 Location {
4547 buffer: start_buffer,
4548 range: start..end,
4549 },
4550 actions.into(),
4551 ))
4552 };
4553 cx.notify();
4554 })
4555 }));
4556 None
4557 }
4558
4559 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4560 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4561 self.show_git_blame_inline = false;
4562
4563 self.show_git_blame_inline_delay_task =
4564 Some(cx.spawn_in(window, |this, mut cx| async move {
4565 cx.background_executor().timer(delay).await;
4566
4567 this.update(&mut cx, |this, cx| {
4568 this.show_git_blame_inline = true;
4569 cx.notify();
4570 })
4571 .log_err();
4572 }));
4573 }
4574 }
4575
4576 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4577 if self.pending_rename.is_some() {
4578 return None;
4579 }
4580
4581 let provider = self.semantics_provider.clone()?;
4582 let buffer = self.buffer.read(cx);
4583 let newest_selection = self.selections.newest_anchor().clone();
4584 let cursor_position = newest_selection.head();
4585 let (cursor_buffer, cursor_buffer_position) =
4586 buffer.text_anchor_for_position(cursor_position, cx)?;
4587 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4588 if cursor_buffer != tail_buffer {
4589 return None;
4590 }
4591 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4592 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4593 cx.background_executor()
4594 .timer(Duration::from_millis(debounce))
4595 .await;
4596
4597 let highlights = if let Some(highlights) = cx
4598 .update(|cx| {
4599 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4600 })
4601 .ok()
4602 .flatten()
4603 {
4604 highlights.await.log_err()
4605 } else {
4606 None
4607 };
4608
4609 if let Some(highlights) = highlights {
4610 this.update(&mut cx, |this, cx| {
4611 if this.pending_rename.is_some() {
4612 return;
4613 }
4614
4615 let buffer_id = cursor_position.buffer_id;
4616 let buffer = this.buffer.read(cx);
4617 if !buffer
4618 .text_anchor_for_position(cursor_position, cx)
4619 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4620 {
4621 return;
4622 }
4623
4624 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4625 let mut write_ranges = Vec::new();
4626 let mut read_ranges = Vec::new();
4627 for highlight in highlights {
4628 for (excerpt_id, excerpt_range) in
4629 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4630 {
4631 let start = highlight
4632 .range
4633 .start
4634 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4635 let end = highlight
4636 .range
4637 .end
4638 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4639 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4640 continue;
4641 }
4642
4643 let range = Anchor {
4644 buffer_id,
4645 excerpt_id,
4646 text_anchor: start,
4647 diff_base_anchor: None,
4648 }..Anchor {
4649 buffer_id,
4650 excerpt_id,
4651 text_anchor: end,
4652 diff_base_anchor: None,
4653 };
4654 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4655 write_ranges.push(range);
4656 } else {
4657 read_ranges.push(range);
4658 }
4659 }
4660 }
4661
4662 this.highlight_background::<DocumentHighlightRead>(
4663 &read_ranges,
4664 |theme| theme.editor_document_highlight_read_background,
4665 cx,
4666 );
4667 this.highlight_background::<DocumentHighlightWrite>(
4668 &write_ranges,
4669 |theme| theme.editor_document_highlight_write_background,
4670 cx,
4671 );
4672 cx.notify();
4673 })
4674 .log_err();
4675 }
4676 }));
4677 None
4678 }
4679
4680 pub fn refresh_inline_completion(
4681 &mut self,
4682 debounce: bool,
4683 user_requested: bool,
4684 window: &mut Window,
4685 cx: &mut Context<Self>,
4686 ) -> Option<()> {
4687 let provider = self.edit_prediction_provider()?;
4688 let cursor = self.selections.newest_anchor().head();
4689 let (buffer, cursor_buffer_position) =
4690 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4691
4692 if !self.inline_completions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
4693 self.discard_inline_completion(false, cx);
4694 return None;
4695 }
4696
4697 if !user_requested
4698 && (!self.should_show_edit_predictions()
4699 || !self.is_focused(window)
4700 || buffer.read(cx).is_empty())
4701 {
4702 self.discard_inline_completion(false, cx);
4703 return None;
4704 }
4705
4706 self.update_visible_inline_completion(window, cx);
4707 provider.refresh(
4708 self.project.clone(),
4709 buffer,
4710 cursor_buffer_position,
4711 debounce,
4712 cx,
4713 );
4714 Some(())
4715 }
4716
4717 fn show_edit_predictions_in_menu(&self) -> bool {
4718 match self.edit_prediction_settings {
4719 EditPredictionSettings::Disabled => false,
4720 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
4721 }
4722 }
4723
4724 pub fn edit_predictions_enabled(&self) -> bool {
4725 match self.edit_prediction_settings {
4726 EditPredictionSettings::Disabled => false,
4727 EditPredictionSettings::Enabled { .. } => true,
4728 }
4729 }
4730
4731 fn edit_prediction_requires_modifier(&self) -> bool {
4732 match self.edit_prediction_settings {
4733 EditPredictionSettings::Disabled => false,
4734 EditPredictionSettings::Enabled {
4735 preview_requires_modifier,
4736 ..
4737 } => preview_requires_modifier,
4738 }
4739 }
4740
4741 fn edit_prediction_settings_at_position(
4742 &self,
4743 buffer: &Entity<Buffer>,
4744 buffer_position: language::Anchor,
4745 cx: &App,
4746 ) -> EditPredictionSettings {
4747 if self.mode != EditorMode::Full
4748 || !self.show_inline_completions_override.unwrap_or(true)
4749 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
4750 {
4751 return EditPredictionSettings::Disabled;
4752 }
4753
4754 let buffer = buffer.read(cx);
4755
4756 let file = buffer.file();
4757
4758 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
4759 return EditPredictionSettings::Disabled;
4760 };
4761
4762 let by_provider = matches!(
4763 self.menu_inline_completions_policy,
4764 MenuInlineCompletionsPolicy::ByProvider
4765 );
4766
4767 let show_in_menu = by_provider
4768 && EditorSettings::get_global(cx).show_edit_predictions_in_menu
4769 && self
4770 .edit_prediction_provider
4771 .as_ref()
4772 .map_or(false, |provider| {
4773 provider.provider.show_completions_in_menu()
4774 });
4775
4776 let preview_requires_modifier = all_language_settings(file, cx)
4777 .inline_completions_preview_mode()
4778 == InlineCompletionPreviewMode::WhenHoldingModifier;
4779
4780 EditPredictionSettings::Enabled {
4781 show_in_menu,
4782 preview_requires_modifier,
4783 }
4784 }
4785
4786 fn should_show_edit_predictions(&self) -> bool {
4787 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
4788 }
4789
4790 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
4791 let cursor = self.selections.newest_anchor().head();
4792 if let Some((buffer, cursor_position)) =
4793 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
4794 {
4795 self.inline_completions_enabled_in_buffer(&buffer, cursor_position, cx)
4796 } else {
4797 false
4798 }
4799 }
4800
4801 fn inline_completions_enabled_in_buffer(
4802 &self,
4803 buffer: &Entity<Buffer>,
4804 buffer_position: language::Anchor,
4805 cx: &App,
4806 ) -> bool {
4807 maybe!({
4808 let provider = self.edit_prediction_provider()?;
4809 if !provider.is_enabled(&buffer, buffer_position, cx) {
4810 return Some(false);
4811 }
4812 let buffer = buffer.read(cx);
4813 let Some(file) = buffer.file() else {
4814 return Some(true);
4815 };
4816 let settings = all_language_settings(Some(file), cx);
4817 Some(settings.inline_completions_enabled_for_path(file.path()))
4818 })
4819 .unwrap_or(false)
4820 }
4821
4822 fn cycle_inline_completion(
4823 &mut self,
4824 direction: Direction,
4825 window: &mut Window,
4826 cx: &mut Context<Self>,
4827 ) -> Option<()> {
4828 let provider = self.edit_prediction_provider()?;
4829 let cursor = self.selections.newest_anchor().head();
4830 let (buffer, cursor_buffer_position) =
4831 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4832 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
4833 return None;
4834 }
4835
4836 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4837 self.update_visible_inline_completion(window, cx);
4838
4839 Some(())
4840 }
4841
4842 pub fn show_inline_completion(
4843 &mut self,
4844 _: &ShowEditPrediction,
4845 window: &mut Window,
4846 cx: &mut Context<Self>,
4847 ) {
4848 if !self.has_active_inline_completion() {
4849 self.refresh_inline_completion(false, true, window, cx);
4850 return;
4851 }
4852
4853 self.update_visible_inline_completion(window, cx);
4854 }
4855
4856 pub fn display_cursor_names(
4857 &mut self,
4858 _: &DisplayCursorNames,
4859 window: &mut Window,
4860 cx: &mut Context<Self>,
4861 ) {
4862 self.show_cursor_names(window, cx);
4863 }
4864
4865 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4866 self.show_cursor_names = true;
4867 cx.notify();
4868 cx.spawn_in(window, |this, mut cx| async move {
4869 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4870 this.update(&mut cx, |this, cx| {
4871 this.show_cursor_names = false;
4872 cx.notify()
4873 })
4874 .ok()
4875 })
4876 .detach();
4877 }
4878
4879 pub fn next_edit_prediction(
4880 &mut self,
4881 _: &NextEditPrediction,
4882 window: &mut Window,
4883 cx: &mut Context<Self>,
4884 ) {
4885 if self.has_active_inline_completion() {
4886 self.cycle_inline_completion(Direction::Next, window, cx);
4887 } else {
4888 let is_copilot_disabled = self
4889 .refresh_inline_completion(false, true, window, cx)
4890 .is_none();
4891 if is_copilot_disabled {
4892 cx.propagate();
4893 }
4894 }
4895 }
4896
4897 pub fn previous_edit_prediction(
4898 &mut self,
4899 _: &PreviousEditPrediction,
4900 window: &mut Window,
4901 cx: &mut Context<Self>,
4902 ) {
4903 if self.has_active_inline_completion() {
4904 self.cycle_inline_completion(Direction::Prev, window, cx);
4905 } else {
4906 let is_copilot_disabled = self
4907 .refresh_inline_completion(false, true, window, cx)
4908 .is_none();
4909 if is_copilot_disabled {
4910 cx.propagate();
4911 }
4912 }
4913 }
4914
4915 pub fn accept_edit_prediction(
4916 &mut self,
4917 _: &AcceptEditPrediction,
4918 window: &mut Window,
4919 cx: &mut Context<Self>,
4920 ) {
4921 let buffer = self.buffer.read(cx);
4922 let snapshot = buffer.snapshot(cx);
4923 let selection = self.selections.newest_adjusted(cx);
4924 let cursor = selection.head();
4925 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
4926 let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
4927 if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
4928 {
4929 if cursor.column < suggested_indent.len
4930 && cursor.column <= current_indent.len
4931 && current_indent.len <= suggested_indent.len
4932 {
4933 self.tab(&Default::default(), window, cx);
4934 return;
4935 }
4936 }
4937
4938 if self.show_edit_predictions_in_menu() {
4939 self.hide_context_menu(window, cx);
4940 }
4941
4942 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4943 return;
4944 };
4945
4946 self.report_inline_completion_event(
4947 active_inline_completion.completion_id.clone(),
4948 true,
4949 cx,
4950 );
4951
4952 match &active_inline_completion.completion {
4953 InlineCompletion::Move { target, .. } => {
4954 let target = *target;
4955 // Note that this is also done in vim's handler of the Tab action.
4956 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
4957 selections.select_anchor_ranges([target..target]);
4958 });
4959 }
4960 InlineCompletion::Edit { edits, .. } => {
4961 if let Some(provider) = self.edit_prediction_provider() {
4962 provider.accept(cx);
4963 }
4964
4965 let snapshot = self.buffer.read(cx).snapshot(cx);
4966 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4967
4968 self.buffer.update(cx, |buffer, cx| {
4969 buffer.edit(edits.iter().cloned(), None, cx)
4970 });
4971
4972 self.change_selections(None, window, cx, |s| {
4973 s.select_anchor_ranges([last_edit_end..last_edit_end])
4974 });
4975
4976 self.update_visible_inline_completion(window, cx);
4977 if self.active_inline_completion.is_none() {
4978 self.refresh_inline_completion(true, true, window, cx);
4979 }
4980
4981 cx.notify();
4982 }
4983 }
4984 }
4985
4986 pub fn accept_partial_inline_completion(
4987 &mut self,
4988 _: &AcceptPartialEditPrediction,
4989 window: &mut Window,
4990 cx: &mut Context<Self>,
4991 ) {
4992 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4993 return;
4994 };
4995 if self.selections.count() != 1 {
4996 return;
4997 }
4998
4999 self.report_inline_completion_event(
5000 active_inline_completion.completion_id.clone(),
5001 true,
5002 cx,
5003 );
5004
5005 match &active_inline_completion.completion {
5006 InlineCompletion::Move { target, .. } => {
5007 let target = *target;
5008 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5009 selections.select_anchor_ranges([target..target]);
5010 });
5011 }
5012 InlineCompletion::Edit { edits, .. } => {
5013 // Find an insertion that starts at the cursor position.
5014 let snapshot = self.buffer.read(cx).snapshot(cx);
5015 let cursor_offset = self.selections.newest::<usize>(cx).head();
5016 let insertion = edits.iter().find_map(|(range, text)| {
5017 let range = range.to_offset(&snapshot);
5018 if range.is_empty() && range.start == cursor_offset {
5019 Some(text)
5020 } else {
5021 None
5022 }
5023 });
5024
5025 if let Some(text) = insertion {
5026 let mut partial_completion = text
5027 .chars()
5028 .by_ref()
5029 .take_while(|c| c.is_alphabetic())
5030 .collect::<String>();
5031 if partial_completion.is_empty() {
5032 partial_completion = text
5033 .chars()
5034 .by_ref()
5035 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5036 .collect::<String>();
5037 }
5038
5039 cx.emit(EditorEvent::InputHandled {
5040 utf16_range_to_replace: None,
5041 text: partial_completion.clone().into(),
5042 });
5043
5044 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5045
5046 self.refresh_inline_completion(true, true, window, cx);
5047 cx.notify();
5048 } else {
5049 self.accept_edit_prediction(&Default::default(), window, cx);
5050 }
5051 }
5052 }
5053 }
5054
5055 fn discard_inline_completion(
5056 &mut self,
5057 should_report_inline_completion_event: bool,
5058 cx: &mut Context<Self>,
5059 ) -> bool {
5060 if should_report_inline_completion_event {
5061 let completion_id = self
5062 .active_inline_completion
5063 .as_ref()
5064 .and_then(|active_completion| active_completion.completion_id.clone());
5065
5066 self.report_inline_completion_event(completion_id, false, cx);
5067 }
5068
5069 if let Some(provider) = self.edit_prediction_provider() {
5070 provider.discard(cx);
5071 }
5072
5073 self.take_active_inline_completion(cx)
5074 }
5075
5076 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5077 let Some(provider) = self.edit_prediction_provider() else {
5078 return;
5079 };
5080
5081 let Some((_, buffer, _)) = self
5082 .buffer
5083 .read(cx)
5084 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5085 else {
5086 return;
5087 };
5088
5089 let extension = buffer
5090 .read(cx)
5091 .file()
5092 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5093
5094 let event_type = match accepted {
5095 true => "Edit Prediction Accepted",
5096 false => "Edit Prediction Discarded",
5097 };
5098 telemetry::event!(
5099 event_type,
5100 provider = provider.name(),
5101 prediction_id = id,
5102 suggestion_accepted = accepted,
5103 file_extension = extension,
5104 );
5105 }
5106
5107 pub fn has_active_inline_completion(&self) -> bool {
5108 self.active_inline_completion.is_some()
5109 }
5110
5111 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5112 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5113 return false;
5114 };
5115
5116 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5117 self.clear_highlights::<InlineCompletionHighlight>(cx);
5118 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5119 true
5120 }
5121
5122 /// Returns true when we're displaying the inline completion popover below the cursor
5123 /// like we are not previewing and the LSP autocomplete menu is visible
5124 /// or we are in `when_holding_modifier` mode.
5125 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5126 if self.previewing_inline_completion
5127 || !self.show_edit_predictions_in_menu()
5128 || !self.edit_predictions_enabled()
5129 {
5130 return false;
5131 }
5132
5133 if self.has_visible_completions_menu() {
5134 return true;
5135 }
5136
5137 has_completion && self.edit_prediction_requires_modifier()
5138 }
5139
5140 fn handle_modifiers_changed(
5141 &mut self,
5142 modifiers: Modifiers,
5143 position_map: &PositionMap,
5144 window: &mut Window,
5145 cx: &mut Context<Self>,
5146 ) {
5147 if self.show_edit_predictions_in_menu() {
5148 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
5149 if let Some(accept_keystroke) = accept_binding.keystroke() {
5150 let was_previewing_inline_completion = self.previewing_inline_completion;
5151 self.previewing_inline_completion = modifiers == accept_keystroke.modifiers
5152 && accept_keystroke.modifiers.modified();
5153 if self.previewing_inline_completion != was_previewing_inline_completion {
5154 self.update_visible_inline_completion(window, cx);
5155 }
5156 }
5157 }
5158
5159 let mouse_position = window.mouse_position();
5160 if !position_map.text_hitbox.is_hovered(window) {
5161 return;
5162 }
5163
5164 self.update_hovered_link(
5165 position_map.point_for_position(mouse_position),
5166 &position_map.snapshot,
5167 modifiers,
5168 window,
5169 cx,
5170 )
5171 }
5172
5173 fn update_visible_inline_completion(
5174 &mut self,
5175 _window: &mut Window,
5176 cx: &mut Context<Self>,
5177 ) -> Option<()> {
5178 let selection = self.selections.newest_anchor();
5179 let cursor = selection.head();
5180 let multibuffer = self.buffer.read(cx).snapshot(cx);
5181 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5182 let excerpt_id = cursor.excerpt_id;
5183
5184 let show_in_menu = self.show_edit_predictions_in_menu();
5185 let completions_menu_has_precedence = !show_in_menu
5186 && (self.context_menu.borrow().is_some()
5187 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5188
5189 if completions_menu_has_precedence
5190 || !offset_selection.is_empty()
5191 || self
5192 .active_inline_completion
5193 .as_ref()
5194 .map_or(false, |completion| {
5195 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5196 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5197 !invalidation_range.contains(&offset_selection.head())
5198 })
5199 {
5200 self.discard_inline_completion(false, cx);
5201 return None;
5202 }
5203
5204 self.take_active_inline_completion(cx);
5205 let Some(provider) = self.edit_prediction_provider() else {
5206 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5207 return None;
5208 };
5209
5210 let (buffer, cursor_buffer_position) =
5211 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5212
5213 self.edit_prediction_settings =
5214 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5215
5216 if !self.edit_prediction_settings.is_enabled() {
5217 self.discard_inline_completion(false, cx);
5218 return None;
5219 }
5220
5221 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5222 let edits = inline_completion
5223 .edits
5224 .into_iter()
5225 .flat_map(|(range, new_text)| {
5226 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5227 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5228 Some((start..end, new_text))
5229 })
5230 .collect::<Vec<_>>();
5231 if edits.is_empty() {
5232 return None;
5233 }
5234
5235 let first_edit_start = edits.first().unwrap().0.start;
5236 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5237 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5238
5239 let last_edit_end = edits.last().unwrap().0.end;
5240 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5241 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5242
5243 let cursor_row = cursor.to_point(&multibuffer).row;
5244
5245 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5246
5247 let mut inlay_ids = Vec::new();
5248 let invalidation_row_range;
5249 let move_invalidation_row_range = if cursor_row < edit_start_row {
5250 Some(cursor_row..edit_end_row)
5251 } else if cursor_row > edit_end_row {
5252 Some(edit_start_row..cursor_row)
5253 } else {
5254 None
5255 };
5256 let is_move =
5257 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5258 let completion = if is_move {
5259 invalidation_row_range =
5260 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5261 let target = first_edit_start;
5262 let target_point = text::ToPoint::to_point(&target.text_anchor, &snapshot);
5263 // TODO: Base this off of TreeSitter or word boundaries?
5264 let target_excerpt_begin = snapshot.anchor_before(snapshot.clip_point(
5265 Point::new(target_point.row, target_point.column.saturating_sub(20)),
5266 Bias::Left,
5267 ));
5268 let target_excerpt_end = snapshot.anchor_after(snapshot.clip_point(
5269 Point::new(target_point.row, target_point.column + 20),
5270 Bias::Right,
5271 ));
5272 let range_around_target = target_excerpt_begin..target_excerpt_end;
5273 InlineCompletion::Move {
5274 target,
5275 range_around_target,
5276 snapshot,
5277 }
5278 } else {
5279 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5280 && !self.inline_completions_hidden_for_vim_mode;
5281 if show_completions_in_buffer {
5282 if edits
5283 .iter()
5284 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5285 {
5286 let mut inlays = Vec::new();
5287 for (range, new_text) in &edits {
5288 let inlay = Inlay::inline_completion(
5289 post_inc(&mut self.next_inlay_id),
5290 range.start,
5291 new_text.as_str(),
5292 );
5293 inlay_ids.push(inlay.id);
5294 inlays.push(inlay);
5295 }
5296
5297 self.splice_inlays(&[], inlays, cx);
5298 } else {
5299 let background_color = cx.theme().status().deleted_background;
5300 self.highlight_text::<InlineCompletionHighlight>(
5301 edits.iter().map(|(range, _)| range.clone()).collect(),
5302 HighlightStyle {
5303 background_color: Some(background_color),
5304 ..Default::default()
5305 },
5306 cx,
5307 );
5308 }
5309 }
5310
5311 invalidation_row_range = edit_start_row..edit_end_row;
5312
5313 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5314 if provider.show_tab_accept_marker() {
5315 EditDisplayMode::TabAccept
5316 } else {
5317 EditDisplayMode::Inline
5318 }
5319 } else {
5320 EditDisplayMode::DiffPopover
5321 };
5322
5323 InlineCompletion::Edit {
5324 edits,
5325 edit_preview: inline_completion.edit_preview,
5326 display_mode,
5327 snapshot,
5328 }
5329 };
5330
5331 let invalidation_range = multibuffer
5332 .anchor_before(Point::new(invalidation_row_range.start, 0))
5333 ..multibuffer.anchor_after(Point::new(
5334 invalidation_row_range.end,
5335 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5336 ));
5337
5338 self.stale_inline_completion_in_menu = None;
5339 self.active_inline_completion = Some(InlineCompletionState {
5340 inlay_ids,
5341 completion,
5342 completion_id: inline_completion.id,
5343 invalidation_range,
5344 });
5345
5346 cx.notify();
5347
5348 Some(())
5349 }
5350
5351 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5352 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5353 }
5354
5355 fn render_code_actions_indicator(
5356 &self,
5357 _style: &EditorStyle,
5358 row: DisplayRow,
5359 is_active: bool,
5360 cx: &mut Context<Self>,
5361 ) -> Option<IconButton> {
5362 if self.available_code_actions.is_some() {
5363 Some(
5364 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5365 .shape(ui::IconButtonShape::Square)
5366 .icon_size(IconSize::XSmall)
5367 .icon_color(Color::Muted)
5368 .toggle_state(is_active)
5369 .tooltip({
5370 let focus_handle = self.focus_handle.clone();
5371 move |window, cx| {
5372 Tooltip::for_action_in(
5373 "Toggle Code Actions",
5374 &ToggleCodeActions {
5375 deployed_from_indicator: None,
5376 },
5377 &focus_handle,
5378 window,
5379 cx,
5380 )
5381 }
5382 })
5383 .on_click(cx.listener(move |editor, _e, window, cx| {
5384 window.focus(&editor.focus_handle(cx));
5385 editor.toggle_code_actions(
5386 &ToggleCodeActions {
5387 deployed_from_indicator: Some(row),
5388 },
5389 window,
5390 cx,
5391 );
5392 })),
5393 )
5394 } else {
5395 None
5396 }
5397 }
5398
5399 fn clear_tasks(&mut self) {
5400 self.tasks.clear()
5401 }
5402
5403 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5404 if self.tasks.insert(key, value).is_some() {
5405 // This case should hopefully be rare, but just in case...
5406 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5407 }
5408 }
5409
5410 fn build_tasks_context(
5411 project: &Entity<Project>,
5412 buffer: &Entity<Buffer>,
5413 buffer_row: u32,
5414 tasks: &Arc<RunnableTasks>,
5415 cx: &mut Context<Self>,
5416 ) -> Task<Option<task::TaskContext>> {
5417 let position = Point::new(buffer_row, tasks.column);
5418 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5419 let location = Location {
5420 buffer: buffer.clone(),
5421 range: range_start..range_start,
5422 };
5423 // Fill in the environmental variables from the tree-sitter captures
5424 let mut captured_task_variables = TaskVariables::default();
5425 for (capture_name, value) in tasks.extra_variables.clone() {
5426 captured_task_variables.insert(
5427 task::VariableName::Custom(capture_name.into()),
5428 value.clone(),
5429 );
5430 }
5431 project.update(cx, |project, cx| {
5432 project.task_store().update(cx, |task_store, cx| {
5433 task_store.task_context_for_location(captured_task_variables, location, cx)
5434 })
5435 })
5436 }
5437
5438 pub fn spawn_nearest_task(
5439 &mut self,
5440 action: &SpawnNearestTask,
5441 window: &mut Window,
5442 cx: &mut Context<Self>,
5443 ) {
5444 let Some((workspace, _)) = self.workspace.clone() else {
5445 return;
5446 };
5447 let Some(project) = self.project.clone() else {
5448 return;
5449 };
5450
5451 // Try to find a closest, enclosing node using tree-sitter that has a
5452 // task
5453 let Some((buffer, buffer_row, tasks)) = self
5454 .find_enclosing_node_task(cx)
5455 // Or find the task that's closest in row-distance.
5456 .or_else(|| self.find_closest_task(cx))
5457 else {
5458 return;
5459 };
5460
5461 let reveal_strategy = action.reveal;
5462 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5463 cx.spawn_in(window, |_, mut cx| async move {
5464 let context = task_context.await?;
5465 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5466
5467 let resolved = resolved_task.resolved.as_mut()?;
5468 resolved.reveal = reveal_strategy;
5469
5470 workspace
5471 .update(&mut cx, |workspace, cx| {
5472 workspace::tasks::schedule_resolved_task(
5473 workspace,
5474 task_source_kind,
5475 resolved_task,
5476 false,
5477 cx,
5478 );
5479 })
5480 .ok()
5481 })
5482 .detach();
5483 }
5484
5485 fn find_closest_task(
5486 &mut self,
5487 cx: &mut Context<Self>,
5488 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5489 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5490
5491 let ((buffer_id, row), tasks) = self
5492 .tasks
5493 .iter()
5494 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5495
5496 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5497 let tasks = Arc::new(tasks.to_owned());
5498 Some((buffer, *row, tasks))
5499 }
5500
5501 fn find_enclosing_node_task(
5502 &mut self,
5503 cx: &mut Context<Self>,
5504 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5505 let snapshot = self.buffer.read(cx).snapshot(cx);
5506 let offset = self.selections.newest::<usize>(cx).head();
5507 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5508 let buffer_id = excerpt.buffer().remote_id();
5509
5510 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5511 let mut cursor = layer.node().walk();
5512
5513 while cursor.goto_first_child_for_byte(offset).is_some() {
5514 if cursor.node().end_byte() == offset {
5515 cursor.goto_next_sibling();
5516 }
5517 }
5518
5519 // Ascend to the smallest ancestor that contains the range and has a task.
5520 loop {
5521 let node = cursor.node();
5522 let node_range = node.byte_range();
5523 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5524
5525 // Check if this node contains our offset
5526 if node_range.start <= offset && node_range.end >= offset {
5527 // If it contains offset, check for task
5528 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5529 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5530 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5531 }
5532 }
5533
5534 if !cursor.goto_parent() {
5535 break;
5536 }
5537 }
5538 None
5539 }
5540
5541 fn render_run_indicator(
5542 &self,
5543 _style: &EditorStyle,
5544 is_active: bool,
5545 row: DisplayRow,
5546 cx: &mut Context<Self>,
5547 ) -> IconButton {
5548 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5549 .shape(ui::IconButtonShape::Square)
5550 .icon_size(IconSize::XSmall)
5551 .icon_color(Color::Muted)
5552 .toggle_state(is_active)
5553 .on_click(cx.listener(move |editor, _e, window, cx| {
5554 window.focus(&editor.focus_handle(cx));
5555 editor.toggle_code_actions(
5556 &ToggleCodeActions {
5557 deployed_from_indicator: Some(row),
5558 },
5559 window,
5560 cx,
5561 );
5562 }))
5563 }
5564
5565 pub fn context_menu_visible(&self) -> bool {
5566 !self.previewing_inline_completion
5567 && self
5568 .context_menu
5569 .borrow()
5570 .as_ref()
5571 .map_or(false, |menu| menu.visible())
5572 }
5573
5574 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5575 self.context_menu
5576 .borrow()
5577 .as_ref()
5578 .map(|menu| menu.origin())
5579 }
5580
5581 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
5582 px(30.)
5583 }
5584
5585 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
5586 if self.read_only(cx) {
5587 cx.theme().players().read_only()
5588 } else {
5589 self.style.as_ref().unwrap().local_player
5590 }
5591 }
5592
5593 #[allow(clippy::too_many_arguments)]
5594 fn render_edit_prediction_cursor_popover(
5595 &self,
5596 min_width: Pixels,
5597 max_width: Pixels,
5598 cursor_point: Point,
5599 style: &EditorStyle,
5600 accept_keystroke: &gpui::Keystroke,
5601 window: &Window,
5602 cx: &mut Context<Editor>,
5603 ) -> Option<AnyElement> {
5604 let provider = self.edit_prediction_provider.as_ref()?;
5605
5606 if provider.provider.needs_terms_acceptance(cx) {
5607 return Some(
5608 h_flex()
5609 .min_w(min_width)
5610 .flex_1()
5611 .px_2()
5612 .py_1()
5613 .gap_3()
5614 .elevation_2(cx)
5615 .hover(|style| style.bg(cx.theme().colors().element_hover))
5616 .id("accept-terms")
5617 .cursor_pointer()
5618 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
5619 .on_click(cx.listener(|this, _event, window, cx| {
5620 cx.stop_propagation();
5621 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
5622 window.dispatch_action(
5623 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
5624 cx,
5625 );
5626 }))
5627 .child(
5628 h_flex()
5629 .flex_1()
5630 .gap_2()
5631 .child(Icon::new(IconName::ZedPredict))
5632 .child(Label::new("Accept Terms of Service"))
5633 .child(div().w_full())
5634 .child(
5635 Icon::new(IconName::ArrowUpRight)
5636 .color(Color::Muted)
5637 .size(IconSize::Small),
5638 )
5639 .into_any_element(),
5640 )
5641 .into_any(),
5642 );
5643 }
5644
5645 let is_refreshing = provider.provider.is_refreshing(cx);
5646
5647 fn pending_completion_container() -> Div {
5648 h_flex()
5649 .h_full()
5650 .flex_1()
5651 .gap_2()
5652 .child(Icon::new(IconName::ZedPredict))
5653 }
5654
5655 let completion = match &self.active_inline_completion {
5656 Some(completion) => self.render_edit_prediction_cursor_popover_preview(
5657 completion,
5658 cursor_point,
5659 style,
5660 window,
5661 cx,
5662 )?,
5663
5664 None if is_refreshing => match &self.stale_inline_completion_in_menu {
5665 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
5666 stale_completion,
5667 cursor_point,
5668 style,
5669 window,
5670 cx,
5671 )?,
5672
5673 None => {
5674 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
5675 }
5676 },
5677
5678 None => pending_completion_container().child(Label::new("No Prediction")),
5679 };
5680
5681 let buffer_font = theme::ThemeSettings::get_global(cx).buffer_font.clone();
5682 let completion = completion.font(buffer_font.clone());
5683
5684 let completion = if is_refreshing {
5685 completion
5686 .with_animation(
5687 "loading-completion",
5688 Animation::new(Duration::from_secs(2))
5689 .repeat()
5690 .with_easing(pulsating_between(0.4, 0.8)),
5691 |label, delta| label.opacity(delta),
5692 )
5693 .into_any_element()
5694 } else {
5695 completion.into_any_element()
5696 };
5697
5698 let has_completion = self.active_inline_completion.is_some();
5699
5700 Some(
5701 h_flex()
5702 .min_w(min_width)
5703 .max_w(max_width)
5704 .flex_1()
5705 .px_2()
5706 .py_1()
5707 .elevation_2(cx)
5708 .child(completion)
5709 .child(ui::Divider::vertical())
5710 .child(
5711 h_flex()
5712 .h_full()
5713 .gap_1()
5714 .pl_2()
5715 .child(h_flex().font(buffer_font.clone()).gap_1().children(
5716 ui::render_modifiers(
5717 &accept_keystroke.modifiers,
5718 PlatformStyle::platform(),
5719 Some(if !has_completion {
5720 Color::Muted
5721 } else {
5722 Color::Default
5723 }),
5724 None,
5725 true,
5726 ),
5727 ))
5728 .child(Label::new("Preview").into_any_element())
5729 .opacity(if has_completion { 1.0 } else { 0.4 }),
5730 )
5731 .into_any(),
5732 )
5733 }
5734
5735 fn render_edit_prediction_cursor_popover_preview(
5736 &self,
5737 completion: &InlineCompletionState,
5738 cursor_point: Point,
5739 style: &EditorStyle,
5740 window: &Window,
5741 cx: &mut Context<Editor>,
5742 ) -> Option<Div> {
5743 use text::ToPoint as _;
5744
5745 fn render_relative_row_jump(
5746 prefix: impl Into<String>,
5747 current_row: u32,
5748 target_row: u32,
5749 ) -> Div {
5750 let (row_diff, arrow) = if target_row < current_row {
5751 (current_row - target_row, IconName::ArrowUp)
5752 } else {
5753 (target_row - current_row, IconName::ArrowDown)
5754 };
5755
5756 h_flex()
5757 .child(
5758 Label::new(format!("{}{}", prefix.into(), row_diff))
5759 .color(Color::Muted)
5760 .size(LabelSize::Small),
5761 )
5762 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
5763 }
5764
5765 match &completion.completion {
5766 InlineCompletion::Edit {
5767 edits,
5768 edit_preview,
5769 snapshot,
5770 display_mode: _,
5771 } => {
5772 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
5773
5774 let highlighted_edits = crate::inline_completion_edit_text(
5775 &snapshot,
5776 &edits,
5777 edit_preview.as_ref()?,
5778 true,
5779 cx,
5780 );
5781
5782 let len_total = highlighted_edits.text.len();
5783 let first_line = &highlighted_edits.text
5784 [..highlighted_edits.text.find('\n').unwrap_or(len_total)];
5785 let first_line_len = first_line.len();
5786
5787 let first_highlight_start = highlighted_edits
5788 .highlights
5789 .first()
5790 .map_or(0, |(range, _)| range.start);
5791 let drop_prefix_len = first_line
5792 .char_indices()
5793 .find(|(_, c)| !c.is_whitespace())
5794 .map_or(first_highlight_start, |(ix, _)| {
5795 ix.min(first_highlight_start)
5796 });
5797
5798 let preview_text = &first_line[drop_prefix_len..];
5799 let preview_len = preview_text.len();
5800 let highlights = highlighted_edits
5801 .highlights
5802 .into_iter()
5803 .take_until(|(range, _)| range.start > first_line_len)
5804 .map(|(range, style)| {
5805 (
5806 range.start - drop_prefix_len
5807 ..(range.end - drop_prefix_len).min(preview_len),
5808 style,
5809 )
5810 });
5811
5812 let styled_text = gpui::StyledText::new(SharedString::new(preview_text))
5813 .with_highlights(&style.text, highlights);
5814
5815 let preview = h_flex()
5816 .gap_1()
5817 .min_w_16()
5818 .child(styled_text)
5819 .when(len_total > first_line_len, |parent| parent.child("…"));
5820
5821 let left = if first_edit_row != cursor_point.row {
5822 render_relative_row_jump("", cursor_point.row, first_edit_row)
5823 .into_any_element()
5824 } else {
5825 Icon::new(IconName::ZedPredict).into_any_element()
5826 };
5827
5828 Some(
5829 h_flex()
5830 .h_full()
5831 .flex_1()
5832 .gap_2()
5833 .pr_1()
5834 .overflow_x_hidden()
5835 .child(left)
5836 .child(preview),
5837 )
5838 }
5839
5840 InlineCompletion::Move {
5841 target,
5842 range_around_target,
5843 snapshot,
5844 } => {
5845 let highlighted_text = snapshot.highlighted_text_for_range(
5846 range_around_target.clone(),
5847 None,
5848 &style.syntax,
5849 );
5850 let base = h_flex().gap_3().flex_1().child(render_relative_row_jump(
5851 "Jump ",
5852 cursor_point.row,
5853 target.text_anchor.to_point(&snapshot).row,
5854 ));
5855
5856 if highlighted_text.text.is_empty() {
5857 return Some(base);
5858 }
5859
5860 let cursor_color = self.current_user_player_color(cx).cursor;
5861
5862 let start_point = range_around_target.start.to_point(&snapshot);
5863 let end_point = range_around_target.end.to_point(&snapshot);
5864 let target_point = target.text_anchor.to_point(&snapshot);
5865
5866 let styled_text = highlighted_text.to_styled_text(&style.text);
5867 let text_len = highlighted_text.text.len();
5868
5869 let cursor_relative_position = window
5870 .text_system()
5871 .layout_line(
5872 highlighted_text.text,
5873 style.text.font_size.to_pixels(window.rem_size()),
5874 // We don't need to include highlights
5875 // because we are only using this for the cursor position
5876 &[TextRun {
5877 len: text_len,
5878 font: style.text.font(),
5879 color: style.text.color,
5880 background_color: None,
5881 underline: None,
5882 strikethrough: None,
5883 }],
5884 )
5885 .log_err()
5886 .map(|line| {
5887 line.x_for_index(
5888 target_point.column.saturating_sub(start_point.column) as usize
5889 )
5890 });
5891
5892 let fade_before = start_point.column > 0;
5893 let fade_after = end_point.column < snapshot.line_len(end_point.row);
5894
5895 let background = cx.theme().colors().elevated_surface_background;
5896
5897 let preview = h_flex()
5898 .relative()
5899 .child(styled_text)
5900 .when(fade_before, |parent| {
5901 parent.child(div().absolute().top_0().left_0().w_4().h_full().bg(
5902 linear_gradient(
5903 90.,
5904 linear_color_stop(background, 0.),
5905 linear_color_stop(background.opacity(0.), 1.),
5906 ),
5907 ))
5908 })
5909 .when(fade_after, |parent| {
5910 parent.child(div().absolute().top_0().right_0().w_4().h_full().bg(
5911 linear_gradient(
5912 -90.,
5913 linear_color_stop(background, 0.),
5914 linear_color_stop(background.opacity(0.), 1.),
5915 ),
5916 ))
5917 })
5918 .when_some(cursor_relative_position, |parent, position| {
5919 parent.child(
5920 div()
5921 .w(px(2.))
5922 .h_full()
5923 .bg(cursor_color)
5924 .absolute()
5925 .top_0()
5926 .left(position),
5927 )
5928 });
5929
5930 Some(base.child(preview))
5931 }
5932 }
5933 }
5934
5935 fn render_context_menu(
5936 &self,
5937 style: &EditorStyle,
5938 max_height_in_lines: u32,
5939 y_flipped: bool,
5940 window: &mut Window,
5941 cx: &mut Context<Editor>,
5942 ) -> Option<AnyElement> {
5943 let menu = self.context_menu.borrow();
5944 let menu = menu.as_ref()?;
5945 if !menu.visible() {
5946 return None;
5947 };
5948 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
5949 }
5950
5951 fn render_context_menu_aside(
5952 &self,
5953 style: &EditorStyle,
5954 max_size: Size<Pixels>,
5955 cx: &mut Context<Editor>,
5956 ) -> Option<AnyElement> {
5957 self.context_menu.borrow().as_ref().and_then(|menu| {
5958 if menu.visible() {
5959 menu.render_aside(
5960 style,
5961 max_size,
5962 self.workspace.as_ref().map(|(w, _)| w.clone()),
5963 cx,
5964 )
5965 } else {
5966 None
5967 }
5968 })
5969 }
5970
5971 fn hide_context_menu(
5972 &mut self,
5973 window: &mut Window,
5974 cx: &mut Context<Self>,
5975 ) -> Option<CodeContextMenu> {
5976 cx.notify();
5977 self.completion_tasks.clear();
5978 let context_menu = self.context_menu.borrow_mut().take();
5979 self.stale_inline_completion_in_menu.take();
5980 self.update_visible_inline_completion(window, cx);
5981 context_menu
5982 }
5983
5984 fn show_snippet_choices(
5985 &mut self,
5986 choices: &Vec<String>,
5987 selection: Range<Anchor>,
5988 cx: &mut Context<Self>,
5989 ) {
5990 if selection.start.buffer_id.is_none() {
5991 return;
5992 }
5993 let buffer_id = selection.start.buffer_id.unwrap();
5994 let buffer = self.buffer().read(cx).buffer(buffer_id);
5995 let id = post_inc(&mut self.next_completion_id);
5996
5997 if let Some(buffer) = buffer {
5998 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
5999 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
6000 ));
6001 }
6002 }
6003
6004 pub fn insert_snippet(
6005 &mut self,
6006 insertion_ranges: &[Range<usize>],
6007 snippet: Snippet,
6008 window: &mut Window,
6009 cx: &mut Context<Self>,
6010 ) -> Result<()> {
6011 struct Tabstop<T> {
6012 is_end_tabstop: bool,
6013 ranges: Vec<Range<T>>,
6014 choices: Option<Vec<String>>,
6015 }
6016
6017 let tabstops = self.buffer.update(cx, |buffer, cx| {
6018 let snippet_text: Arc<str> = snippet.text.clone().into();
6019 buffer.edit(
6020 insertion_ranges
6021 .iter()
6022 .cloned()
6023 .map(|range| (range, snippet_text.clone())),
6024 Some(AutoindentMode::EachLine),
6025 cx,
6026 );
6027
6028 let snapshot = &*buffer.read(cx);
6029 let snippet = &snippet;
6030 snippet
6031 .tabstops
6032 .iter()
6033 .map(|tabstop| {
6034 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
6035 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
6036 });
6037 let mut tabstop_ranges = tabstop
6038 .ranges
6039 .iter()
6040 .flat_map(|tabstop_range| {
6041 let mut delta = 0_isize;
6042 insertion_ranges.iter().map(move |insertion_range| {
6043 let insertion_start = insertion_range.start as isize + delta;
6044 delta +=
6045 snippet.text.len() as isize - insertion_range.len() as isize;
6046
6047 let start = ((insertion_start + tabstop_range.start) as usize)
6048 .min(snapshot.len());
6049 let end = ((insertion_start + tabstop_range.end) as usize)
6050 .min(snapshot.len());
6051 snapshot.anchor_before(start)..snapshot.anchor_after(end)
6052 })
6053 })
6054 .collect::<Vec<_>>();
6055 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
6056
6057 Tabstop {
6058 is_end_tabstop,
6059 ranges: tabstop_ranges,
6060 choices: tabstop.choices.clone(),
6061 }
6062 })
6063 .collect::<Vec<_>>()
6064 });
6065 if let Some(tabstop) = tabstops.first() {
6066 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6067 s.select_ranges(tabstop.ranges.iter().cloned());
6068 });
6069
6070 if let Some(choices) = &tabstop.choices {
6071 if let Some(selection) = tabstop.ranges.first() {
6072 self.show_snippet_choices(choices, selection.clone(), cx)
6073 }
6074 }
6075
6076 // If we're already at the last tabstop and it's at the end of the snippet,
6077 // we're done, we don't need to keep the state around.
6078 if !tabstop.is_end_tabstop {
6079 let choices = tabstops
6080 .iter()
6081 .map(|tabstop| tabstop.choices.clone())
6082 .collect();
6083
6084 let ranges = tabstops
6085 .into_iter()
6086 .map(|tabstop| tabstop.ranges)
6087 .collect::<Vec<_>>();
6088
6089 self.snippet_stack.push(SnippetState {
6090 active_index: 0,
6091 ranges,
6092 choices,
6093 });
6094 }
6095
6096 // Check whether the just-entered snippet ends with an auto-closable bracket.
6097 if self.autoclose_regions.is_empty() {
6098 let snapshot = self.buffer.read(cx).snapshot(cx);
6099 for selection in &mut self.selections.all::<Point>(cx) {
6100 let selection_head = selection.head();
6101 let Some(scope) = snapshot.language_scope_at(selection_head) else {
6102 continue;
6103 };
6104
6105 let mut bracket_pair = None;
6106 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
6107 let prev_chars = snapshot
6108 .reversed_chars_at(selection_head)
6109 .collect::<String>();
6110 for (pair, enabled) in scope.brackets() {
6111 if enabled
6112 && pair.close
6113 && prev_chars.starts_with(pair.start.as_str())
6114 && next_chars.starts_with(pair.end.as_str())
6115 {
6116 bracket_pair = Some(pair.clone());
6117 break;
6118 }
6119 }
6120 if let Some(pair) = bracket_pair {
6121 let start = snapshot.anchor_after(selection_head);
6122 let end = snapshot.anchor_after(selection_head);
6123 self.autoclose_regions.push(AutocloseRegion {
6124 selection_id: selection.id,
6125 range: start..end,
6126 pair,
6127 });
6128 }
6129 }
6130 }
6131 }
6132 Ok(())
6133 }
6134
6135 pub fn move_to_next_snippet_tabstop(
6136 &mut self,
6137 window: &mut Window,
6138 cx: &mut Context<Self>,
6139 ) -> bool {
6140 self.move_to_snippet_tabstop(Bias::Right, window, cx)
6141 }
6142
6143 pub fn move_to_prev_snippet_tabstop(
6144 &mut self,
6145 window: &mut Window,
6146 cx: &mut Context<Self>,
6147 ) -> bool {
6148 self.move_to_snippet_tabstop(Bias::Left, window, cx)
6149 }
6150
6151 pub fn move_to_snippet_tabstop(
6152 &mut self,
6153 bias: Bias,
6154 window: &mut Window,
6155 cx: &mut Context<Self>,
6156 ) -> bool {
6157 if let Some(mut snippet) = self.snippet_stack.pop() {
6158 match bias {
6159 Bias::Left => {
6160 if snippet.active_index > 0 {
6161 snippet.active_index -= 1;
6162 } else {
6163 self.snippet_stack.push(snippet);
6164 return false;
6165 }
6166 }
6167 Bias::Right => {
6168 if snippet.active_index + 1 < snippet.ranges.len() {
6169 snippet.active_index += 1;
6170 } else {
6171 self.snippet_stack.push(snippet);
6172 return false;
6173 }
6174 }
6175 }
6176 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
6177 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6178 s.select_anchor_ranges(current_ranges.iter().cloned())
6179 });
6180
6181 if let Some(choices) = &snippet.choices[snippet.active_index] {
6182 if let Some(selection) = current_ranges.first() {
6183 self.show_snippet_choices(&choices, selection.clone(), cx);
6184 }
6185 }
6186
6187 // If snippet state is not at the last tabstop, push it back on the stack
6188 if snippet.active_index + 1 < snippet.ranges.len() {
6189 self.snippet_stack.push(snippet);
6190 }
6191 return true;
6192 }
6193 }
6194
6195 false
6196 }
6197
6198 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6199 self.transact(window, cx, |this, window, cx| {
6200 this.select_all(&SelectAll, window, cx);
6201 this.insert("", window, cx);
6202 });
6203 }
6204
6205 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
6206 self.transact(window, cx, |this, window, cx| {
6207 this.select_autoclose_pair(window, cx);
6208 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
6209 if !this.linked_edit_ranges.is_empty() {
6210 let selections = this.selections.all::<MultiBufferPoint>(cx);
6211 let snapshot = this.buffer.read(cx).snapshot(cx);
6212
6213 for selection in selections.iter() {
6214 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
6215 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
6216 if selection_start.buffer_id != selection_end.buffer_id {
6217 continue;
6218 }
6219 if let Some(ranges) =
6220 this.linked_editing_ranges_for(selection_start..selection_end, cx)
6221 {
6222 for (buffer, entries) in ranges {
6223 linked_ranges.entry(buffer).or_default().extend(entries);
6224 }
6225 }
6226 }
6227 }
6228
6229 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
6230 if !this.selections.line_mode {
6231 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
6232 for selection in &mut selections {
6233 if selection.is_empty() {
6234 let old_head = selection.head();
6235 let mut new_head =
6236 movement::left(&display_map, old_head.to_display_point(&display_map))
6237 .to_point(&display_map);
6238 if let Some((buffer, line_buffer_range)) = display_map
6239 .buffer_snapshot
6240 .buffer_line_for_row(MultiBufferRow(old_head.row))
6241 {
6242 let indent_size =
6243 buffer.indent_size_for_line(line_buffer_range.start.row);
6244 let indent_len = match indent_size.kind {
6245 IndentKind::Space => {
6246 buffer.settings_at(line_buffer_range.start, cx).tab_size
6247 }
6248 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
6249 };
6250 if old_head.column <= indent_size.len && old_head.column > 0 {
6251 let indent_len = indent_len.get();
6252 new_head = cmp::min(
6253 new_head,
6254 MultiBufferPoint::new(
6255 old_head.row,
6256 ((old_head.column - 1) / indent_len) * indent_len,
6257 ),
6258 );
6259 }
6260 }
6261
6262 selection.set_head(new_head, SelectionGoal::None);
6263 }
6264 }
6265 }
6266
6267 this.signature_help_state.set_backspace_pressed(true);
6268 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6269 s.select(selections)
6270 });
6271 this.insert("", window, cx);
6272 let empty_str: Arc<str> = Arc::from("");
6273 for (buffer, edits) in linked_ranges {
6274 let snapshot = buffer.read(cx).snapshot();
6275 use text::ToPoint as TP;
6276
6277 let edits = edits
6278 .into_iter()
6279 .map(|range| {
6280 let end_point = TP::to_point(&range.end, &snapshot);
6281 let mut start_point = TP::to_point(&range.start, &snapshot);
6282
6283 if end_point == start_point {
6284 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6285 .saturating_sub(1);
6286 start_point =
6287 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
6288 };
6289
6290 (start_point..end_point, empty_str.clone())
6291 })
6292 .sorted_by_key(|(range, _)| range.start)
6293 .collect::<Vec<_>>();
6294 buffer.update(cx, |this, cx| {
6295 this.edit(edits, None, cx);
6296 })
6297 }
6298 this.refresh_inline_completion(true, false, window, cx);
6299 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
6300 });
6301 }
6302
6303 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
6304 self.transact(window, cx, |this, window, cx| {
6305 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6306 let line_mode = s.line_mode;
6307 s.move_with(|map, selection| {
6308 if selection.is_empty() && !line_mode {
6309 let cursor = movement::right(map, selection.head());
6310 selection.end = cursor;
6311 selection.reversed = true;
6312 selection.goal = SelectionGoal::None;
6313 }
6314 })
6315 });
6316 this.insert("", window, cx);
6317 this.refresh_inline_completion(true, false, window, cx);
6318 });
6319 }
6320
6321 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
6322 if self.move_to_prev_snippet_tabstop(window, cx) {
6323 return;
6324 }
6325
6326 self.outdent(&Outdent, window, cx);
6327 }
6328
6329 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
6330 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
6331 return;
6332 }
6333
6334 let mut selections = self.selections.all_adjusted(cx);
6335 let buffer = self.buffer.read(cx);
6336 let snapshot = buffer.snapshot(cx);
6337 let rows_iter = selections.iter().map(|s| s.head().row);
6338 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6339
6340 let mut edits = Vec::new();
6341 let mut prev_edited_row = 0;
6342 let mut row_delta = 0;
6343 for selection in &mut selections {
6344 if selection.start.row != prev_edited_row {
6345 row_delta = 0;
6346 }
6347 prev_edited_row = selection.end.row;
6348
6349 // If the selection is non-empty, then increase the indentation of the selected lines.
6350 if !selection.is_empty() {
6351 row_delta =
6352 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6353 continue;
6354 }
6355
6356 // If the selection is empty and the cursor is in the leading whitespace before the
6357 // suggested indentation, then auto-indent the line.
6358 let cursor = selection.head();
6359 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6360 if let Some(suggested_indent) =
6361 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6362 {
6363 if cursor.column < suggested_indent.len
6364 && cursor.column <= current_indent.len
6365 && current_indent.len <= suggested_indent.len
6366 {
6367 selection.start = Point::new(cursor.row, suggested_indent.len);
6368 selection.end = selection.start;
6369 if row_delta == 0 {
6370 edits.extend(Buffer::edit_for_indent_size_adjustment(
6371 cursor.row,
6372 current_indent,
6373 suggested_indent,
6374 ));
6375 row_delta = suggested_indent.len - current_indent.len;
6376 }
6377 continue;
6378 }
6379 }
6380
6381 // Otherwise, insert a hard or soft tab.
6382 let settings = buffer.settings_at(cursor, cx);
6383 let tab_size = if settings.hard_tabs {
6384 IndentSize::tab()
6385 } else {
6386 let tab_size = settings.tab_size.get();
6387 let char_column = snapshot
6388 .text_for_range(Point::new(cursor.row, 0)..cursor)
6389 .flat_map(str::chars)
6390 .count()
6391 + row_delta as usize;
6392 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6393 IndentSize::spaces(chars_to_next_tab_stop)
6394 };
6395 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6396 selection.end = selection.start;
6397 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6398 row_delta += tab_size.len;
6399 }
6400
6401 self.transact(window, cx, |this, window, cx| {
6402 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6403 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6404 s.select(selections)
6405 });
6406 this.refresh_inline_completion(true, false, window, cx);
6407 });
6408 }
6409
6410 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
6411 if self.read_only(cx) {
6412 return;
6413 }
6414 let mut selections = self.selections.all::<Point>(cx);
6415 let mut prev_edited_row = 0;
6416 let mut row_delta = 0;
6417 let mut edits = Vec::new();
6418 let buffer = self.buffer.read(cx);
6419 let snapshot = buffer.snapshot(cx);
6420 for selection in &mut selections {
6421 if selection.start.row != prev_edited_row {
6422 row_delta = 0;
6423 }
6424 prev_edited_row = selection.end.row;
6425
6426 row_delta =
6427 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6428 }
6429
6430 self.transact(window, cx, |this, window, cx| {
6431 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6432 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6433 s.select(selections)
6434 });
6435 });
6436 }
6437
6438 fn indent_selection(
6439 buffer: &MultiBuffer,
6440 snapshot: &MultiBufferSnapshot,
6441 selection: &mut Selection<Point>,
6442 edits: &mut Vec<(Range<Point>, String)>,
6443 delta_for_start_row: u32,
6444 cx: &App,
6445 ) -> u32 {
6446 let settings = buffer.settings_at(selection.start, cx);
6447 let tab_size = settings.tab_size.get();
6448 let indent_kind = if settings.hard_tabs {
6449 IndentKind::Tab
6450 } else {
6451 IndentKind::Space
6452 };
6453 let mut start_row = selection.start.row;
6454 let mut end_row = selection.end.row + 1;
6455
6456 // If a selection ends at the beginning of a line, don't indent
6457 // that last line.
6458 if selection.end.column == 0 && selection.end.row > selection.start.row {
6459 end_row -= 1;
6460 }
6461
6462 // Avoid re-indenting a row that has already been indented by a
6463 // previous selection, but still update this selection's column
6464 // to reflect that indentation.
6465 if delta_for_start_row > 0 {
6466 start_row += 1;
6467 selection.start.column += delta_for_start_row;
6468 if selection.end.row == selection.start.row {
6469 selection.end.column += delta_for_start_row;
6470 }
6471 }
6472
6473 let mut delta_for_end_row = 0;
6474 let has_multiple_rows = start_row + 1 != end_row;
6475 for row in start_row..end_row {
6476 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6477 let indent_delta = match (current_indent.kind, indent_kind) {
6478 (IndentKind::Space, IndentKind::Space) => {
6479 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6480 IndentSize::spaces(columns_to_next_tab_stop)
6481 }
6482 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6483 (_, IndentKind::Tab) => IndentSize::tab(),
6484 };
6485
6486 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6487 0
6488 } else {
6489 selection.start.column
6490 };
6491 let row_start = Point::new(row, start);
6492 edits.push((
6493 row_start..row_start,
6494 indent_delta.chars().collect::<String>(),
6495 ));
6496
6497 // Update this selection's endpoints to reflect the indentation.
6498 if row == selection.start.row {
6499 selection.start.column += indent_delta.len;
6500 }
6501 if row == selection.end.row {
6502 selection.end.column += indent_delta.len;
6503 delta_for_end_row = indent_delta.len;
6504 }
6505 }
6506
6507 if selection.start.row == selection.end.row {
6508 delta_for_start_row + delta_for_end_row
6509 } else {
6510 delta_for_end_row
6511 }
6512 }
6513
6514 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6515 if self.read_only(cx) {
6516 return;
6517 }
6518 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6519 let selections = self.selections.all::<Point>(cx);
6520 let mut deletion_ranges = Vec::new();
6521 let mut last_outdent = None;
6522 {
6523 let buffer = self.buffer.read(cx);
6524 let snapshot = buffer.snapshot(cx);
6525 for selection in &selections {
6526 let settings = buffer.settings_at(selection.start, cx);
6527 let tab_size = settings.tab_size.get();
6528 let mut rows = selection.spanned_rows(false, &display_map);
6529
6530 // Avoid re-outdenting a row that has already been outdented by a
6531 // previous selection.
6532 if let Some(last_row) = last_outdent {
6533 if last_row == rows.start {
6534 rows.start = rows.start.next_row();
6535 }
6536 }
6537 let has_multiple_rows = rows.len() > 1;
6538 for row in rows.iter_rows() {
6539 let indent_size = snapshot.indent_size_for_line(row);
6540 if indent_size.len > 0 {
6541 let deletion_len = match indent_size.kind {
6542 IndentKind::Space => {
6543 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6544 if columns_to_prev_tab_stop == 0 {
6545 tab_size
6546 } else {
6547 columns_to_prev_tab_stop
6548 }
6549 }
6550 IndentKind::Tab => 1,
6551 };
6552 let start = if has_multiple_rows
6553 || deletion_len > selection.start.column
6554 || indent_size.len < selection.start.column
6555 {
6556 0
6557 } else {
6558 selection.start.column - deletion_len
6559 };
6560 deletion_ranges.push(
6561 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6562 );
6563 last_outdent = Some(row);
6564 }
6565 }
6566 }
6567 }
6568
6569 self.transact(window, cx, |this, window, cx| {
6570 this.buffer.update(cx, |buffer, cx| {
6571 let empty_str: Arc<str> = Arc::default();
6572 buffer.edit(
6573 deletion_ranges
6574 .into_iter()
6575 .map(|range| (range, empty_str.clone())),
6576 None,
6577 cx,
6578 );
6579 });
6580 let selections = this.selections.all::<usize>(cx);
6581 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6582 s.select(selections)
6583 });
6584 });
6585 }
6586
6587 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6588 if self.read_only(cx) {
6589 return;
6590 }
6591 let selections = self
6592 .selections
6593 .all::<usize>(cx)
6594 .into_iter()
6595 .map(|s| s.range());
6596
6597 self.transact(window, cx, |this, window, cx| {
6598 this.buffer.update(cx, |buffer, cx| {
6599 buffer.autoindent_ranges(selections, cx);
6600 });
6601 let selections = this.selections.all::<usize>(cx);
6602 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6603 s.select(selections)
6604 });
6605 });
6606 }
6607
6608 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6609 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6610 let selections = self.selections.all::<Point>(cx);
6611
6612 let mut new_cursors = Vec::new();
6613 let mut edit_ranges = Vec::new();
6614 let mut selections = selections.iter().peekable();
6615 while let Some(selection) = selections.next() {
6616 let mut rows = selection.spanned_rows(false, &display_map);
6617 let goal_display_column = selection.head().to_display_point(&display_map).column();
6618
6619 // Accumulate contiguous regions of rows that we want to delete.
6620 while let Some(next_selection) = selections.peek() {
6621 let next_rows = next_selection.spanned_rows(false, &display_map);
6622 if next_rows.start <= rows.end {
6623 rows.end = next_rows.end;
6624 selections.next().unwrap();
6625 } else {
6626 break;
6627 }
6628 }
6629
6630 let buffer = &display_map.buffer_snapshot;
6631 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6632 let edit_end;
6633 let cursor_buffer_row;
6634 if buffer.max_point().row >= rows.end.0 {
6635 // If there's a line after the range, delete the \n from the end of the row range
6636 // and position the cursor on the next line.
6637 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6638 cursor_buffer_row = rows.end;
6639 } else {
6640 // If there isn't a line after the range, delete the \n from the line before the
6641 // start of the row range and position the cursor there.
6642 edit_start = edit_start.saturating_sub(1);
6643 edit_end = buffer.len();
6644 cursor_buffer_row = rows.start.previous_row();
6645 }
6646
6647 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6648 *cursor.column_mut() =
6649 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6650
6651 new_cursors.push((
6652 selection.id,
6653 buffer.anchor_after(cursor.to_point(&display_map)),
6654 ));
6655 edit_ranges.push(edit_start..edit_end);
6656 }
6657
6658 self.transact(window, cx, |this, window, cx| {
6659 let buffer = this.buffer.update(cx, |buffer, cx| {
6660 let empty_str: Arc<str> = Arc::default();
6661 buffer.edit(
6662 edit_ranges
6663 .into_iter()
6664 .map(|range| (range, empty_str.clone())),
6665 None,
6666 cx,
6667 );
6668 buffer.snapshot(cx)
6669 });
6670 let new_selections = new_cursors
6671 .into_iter()
6672 .map(|(id, cursor)| {
6673 let cursor = cursor.to_point(&buffer);
6674 Selection {
6675 id,
6676 start: cursor,
6677 end: cursor,
6678 reversed: false,
6679 goal: SelectionGoal::None,
6680 }
6681 })
6682 .collect();
6683
6684 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6685 s.select(new_selections);
6686 });
6687 });
6688 }
6689
6690 pub fn join_lines_impl(
6691 &mut self,
6692 insert_whitespace: bool,
6693 window: &mut Window,
6694 cx: &mut Context<Self>,
6695 ) {
6696 if self.read_only(cx) {
6697 return;
6698 }
6699 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6700 for selection in self.selections.all::<Point>(cx) {
6701 let start = MultiBufferRow(selection.start.row);
6702 // Treat single line selections as if they include the next line. Otherwise this action
6703 // would do nothing for single line selections individual cursors.
6704 let end = if selection.start.row == selection.end.row {
6705 MultiBufferRow(selection.start.row + 1)
6706 } else {
6707 MultiBufferRow(selection.end.row)
6708 };
6709
6710 if let Some(last_row_range) = row_ranges.last_mut() {
6711 if start <= last_row_range.end {
6712 last_row_range.end = end;
6713 continue;
6714 }
6715 }
6716 row_ranges.push(start..end);
6717 }
6718
6719 let snapshot = self.buffer.read(cx).snapshot(cx);
6720 let mut cursor_positions = Vec::new();
6721 for row_range in &row_ranges {
6722 let anchor = snapshot.anchor_before(Point::new(
6723 row_range.end.previous_row().0,
6724 snapshot.line_len(row_range.end.previous_row()),
6725 ));
6726 cursor_positions.push(anchor..anchor);
6727 }
6728
6729 self.transact(window, cx, |this, window, cx| {
6730 for row_range in row_ranges.into_iter().rev() {
6731 for row in row_range.iter_rows().rev() {
6732 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6733 let next_line_row = row.next_row();
6734 let indent = snapshot.indent_size_for_line(next_line_row);
6735 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6736
6737 let replace =
6738 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
6739 " "
6740 } else {
6741 ""
6742 };
6743
6744 this.buffer.update(cx, |buffer, cx| {
6745 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6746 });
6747 }
6748 }
6749
6750 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6751 s.select_anchor_ranges(cursor_positions)
6752 });
6753 });
6754 }
6755
6756 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
6757 self.join_lines_impl(true, window, cx);
6758 }
6759
6760 pub fn sort_lines_case_sensitive(
6761 &mut self,
6762 _: &SortLinesCaseSensitive,
6763 window: &mut Window,
6764 cx: &mut Context<Self>,
6765 ) {
6766 self.manipulate_lines(window, cx, |lines| lines.sort())
6767 }
6768
6769 pub fn sort_lines_case_insensitive(
6770 &mut self,
6771 _: &SortLinesCaseInsensitive,
6772 window: &mut Window,
6773 cx: &mut Context<Self>,
6774 ) {
6775 self.manipulate_lines(window, cx, |lines| {
6776 lines.sort_by_key(|line| line.to_lowercase())
6777 })
6778 }
6779
6780 pub fn unique_lines_case_insensitive(
6781 &mut self,
6782 _: &UniqueLinesCaseInsensitive,
6783 window: &mut Window,
6784 cx: &mut Context<Self>,
6785 ) {
6786 self.manipulate_lines(window, cx, |lines| {
6787 let mut seen = HashSet::default();
6788 lines.retain(|line| seen.insert(line.to_lowercase()));
6789 })
6790 }
6791
6792 pub fn unique_lines_case_sensitive(
6793 &mut self,
6794 _: &UniqueLinesCaseSensitive,
6795 window: &mut Window,
6796 cx: &mut Context<Self>,
6797 ) {
6798 self.manipulate_lines(window, cx, |lines| {
6799 let mut seen = HashSet::default();
6800 lines.retain(|line| seen.insert(*line));
6801 })
6802 }
6803
6804 pub fn revert_file(&mut self, _: &RevertFile, window: &mut Window, cx: &mut Context<Self>) {
6805 let mut revert_changes = HashMap::default();
6806 let snapshot = self.snapshot(window, cx);
6807 for hunk in snapshot
6808 .hunks_for_ranges(Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter())
6809 {
6810 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6811 }
6812 if !revert_changes.is_empty() {
6813 self.transact(window, cx, |editor, window, cx| {
6814 editor.revert(revert_changes, window, cx);
6815 });
6816 }
6817 }
6818
6819 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
6820 let Some(project) = self.project.clone() else {
6821 return;
6822 };
6823 self.reload(project, window, cx)
6824 .detach_and_notify_err(window, cx);
6825 }
6826
6827 pub fn revert_selected_hunks(
6828 &mut self,
6829 _: &RevertSelectedHunks,
6830 window: &mut Window,
6831 cx: &mut Context<Self>,
6832 ) {
6833 let selections = self.selections.all(cx).into_iter().map(|s| s.range());
6834 self.revert_hunks_in_ranges(selections, window, cx);
6835 }
6836
6837 fn revert_hunks_in_ranges(
6838 &mut self,
6839 ranges: impl Iterator<Item = Range<Point>>,
6840 window: &mut Window,
6841 cx: &mut Context<Editor>,
6842 ) {
6843 let mut revert_changes = HashMap::default();
6844 let snapshot = self.snapshot(window, cx);
6845 for hunk in &snapshot.hunks_for_ranges(ranges) {
6846 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6847 }
6848 if !revert_changes.is_empty() {
6849 self.transact(window, cx, |editor, window, cx| {
6850 editor.revert(revert_changes, window, cx);
6851 });
6852 }
6853 }
6854
6855 pub fn open_active_item_in_terminal(
6856 &mut self,
6857 _: &OpenInTerminal,
6858 window: &mut Window,
6859 cx: &mut Context<Self>,
6860 ) {
6861 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6862 let project_path = buffer.read(cx).project_path(cx)?;
6863 let project = self.project.as_ref()?.read(cx);
6864 let entry = project.entry_for_path(&project_path, cx)?;
6865 let parent = match &entry.canonical_path {
6866 Some(canonical_path) => canonical_path.to_path_buf(),
6867 None => project.absolute_path(&project_path, cx)?,
6868 }
6869 .parent()?
6870 .to_path_buf();
6871 Some(parent)
6872 }) {
6873 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
6874 }
6875 }
6876
6877 pub fn prepare_revert_change(
6878 &self,
6879 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6880 hunk: &MultiBufferDiffHunk,
6881 cx: &mut App,
6882 ) -> Option<()> {
6883 let buffer = self.buffer.read(cx);
6884 let diff = buffer.diff_for(hunk.buffer_id)?;
6885 let buffer = buffer.buffer(hunk.buffer_id)?;
6886 let buffer = buffer.read(cx);
6887 let original_text = diff
6888 .read(cx)
6889 .snapshot
6890 .base_text
6891 .as_ref()?
6892 .as_rope()
6893 .slice(hunk.diff_base_byte_range.clone());
6894 let buffer_snapshot = buffer.snapshot();
6895 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6896 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6897 probe
6898 .0
6899 .start
6900 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6901 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6902 }) {
6903 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6904 Some(())
6905 } else {
6906 None
6907 }
6908 }
6909
6910 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
6911 self.manipulate_lines(window, cx, |lines| lines.reverse())
6912 }
6913
6914 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
6915 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
6916 }
6917
6918 fn manipulate_lines<Fn>(
6919 &mut self,
6920 window: &mut Window,
6921 cx: &mut Context<Self>,
6922 mut callback: Fn,
6923 ) where
6924 Fn: FnMut(&mut Vec<&str>),
6925 {
6926 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6927 let buffer = self.buffer.read(cx).snapshot(cx);
6928
6929 let mut edits = Vec::new();
6930
6931 let selections = self.selections.all::<Point>(cx);
6932 let mut selections = selections.iter().peekable();
6933 let mut contiguous_row_selections = Vec::new();
6934 let mut new_selections = Vec::new();
6935 let mut added_lines = 0;
6936 let mut removed_lines = 0;
6937
6938 while let Some(selection) = selections.next() {
6939 let (start_row, end_row) = consume_contiguous_rows(
6940 &mut contiguous_row_selections,
6941 selection,
6942 &display_map,
6943 &mut selections,
6944 );
6945
6946 let start_point = Point::new(start_row.0, 0);
6947 let end_point = Point::new(
6948 end_row.previous_row().0,
6949 buffer.line_len(end_row.previous_row()),
6950 );
6951 let text = buffer
6952 .text_for_range(start_point..end_point)
6953 .collect::<String>();
6954
6955 let mut lines = text.split('\n').collect_vec();
6956
6957 let lines_before = lines.len();
6958 callback(&mut lines);
6959 let lines_after = lines.len();
6960
6961 edits.push((start_point..end_point, lines.join("\n")));
6962
6963 // Selections must change based on added and removed line count
6964 let start_row =
6965 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6966 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6967 new_selections.push(Selection {
6968 id: selection.id,
6969 start: start_row,
6970 end: end_row,
6971 goal: SelectionGoal::None,
6972 reversed: selection.reversed,
6973 });
6974
6975 if lines_after > lines_before {
6976 added_lines += lines_after - lines_before;
6977 } else if lines_before > lines_after {
6978 removed_lines += lines_before - lines_after;
6979 }
6980 }
6981
6982 self.transact(window, cx, |this, window, cx| {
6983 let buffer = this.buffer.update(cx, |buffer, cx| {
6984 buffer.edit(edits, None, cx);
6985 buffer.snapshot(cx)
6986 });
6987
6988 // Recalculate offsets on newly edited buffer
6989 let new_selections = new_selections
6990 .iter()
6991 .map(|s| {
6992 let start_point = Point::new(s.start.0, 0);
6993 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6994 Selection {
6995 id: s.id,
6996 start: buffer.point_to_offset(start_point),
6997 end: buffer.point_to_offset(end_point),
6998 goal: s.goal,
6999 reversed: s.reversed,
7000 }
7001 })
7002 .collect();
7003
7004 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7005 s.select(new_selections);
7006 });
7007
7008 this.request_autoscroll(Autoscroll::fit(), cx);
7009 });
7010 }
7011
7012 pub fn convert_to_upper_case(
7013 &mut self,
7014 _: &ConvertToUpperCase,
7015 window: &mut Window,
7016 cx: &mut Context<Self>,
7017 ) {
7018 self.manipulate_text(window, cx, |text| text.to_uppercase())
7019 }
7020
7021 pub fn convert_to_lower_case(
7022 &mut self,
7023 _: &ConvertToLowerCase,
7024 window: &mut Window,
7025 cx: &mut Context<Self>,
7026 ) {
7027 self.manipulate_text(window, cx, |text| text.to_lowercase())
7028 }
7029
7030 pub fn convert_to_title_case(
7031 &mut self,
7032 _: &ConvertToTitleCase,
7033 window: &mut Window,
7034 cx: &mut Context<Self>,
7035 ) {
7036 self.manipulate_text(window, cx, |text| {
7037 text.split('\n')
7038 .map(|line| line.to_case(Case::Title))
7039 .join("\n")
7040 })
7041 }
7042
7043 pub fn convert_to_snake_case(
7044 &mut self,
7045 _: &ConvertToSnakeCase,
7046 window: &mut Window,
7047 cx: &mut Context<Self>,
7048 ) {
7049 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
7050 }
7051
7052 pub fn convert_to_kebab_case(
7053 &mut self,
7054 _: &ConvertToKebabCase,
7055 window: &mut Window,
7056 cx: &mut Context<Self>,
7057 ) {
7058 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
7059 }
7060
7061 pub fn convert_to_upper_camel_case(
7062 &mut self,
7063 _: &ConvertToUpperCamelCase,
7064 window: &mut Window,
7065 cx: &mut Context<Self>,
7066 ) {
7067 self.manipulate_text(window, cx, |text| {
7068 text.split('\n')
7069 .map(|line| line.to_case(Case::UpperCamel))
7070 .join("\n")
7071 })
7072 }
7073
7074 pub fn convert_to_lower_camel_case(
7075 &mut self,
7076 _: &ConvertToLowerCamelCase,
7077 window: &mut Window,
7078 cx: &mut Context<Self>,
7079 ) {
7080 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
7081 }
7082
7083 pub fn convert_to_opposite_case(
7084 &mut self,
7085 _: &ConvertToOppositeCase,
7086 window: &mut Window,
7087 cx: &mut Context<Self>,
7088 ) {
7089 self.manipulate_text(window, cx, |text| {
7090 text.chars()
7091 .fold(String::with_capacity(text.len()), |mut t, c| {
7092 if c.is_uppercase() {
7093 t.extend(c.to_lowercase());
7094 } else {
7095 t.extend(c.to_uppercase());
7096 }
7097 t
7098 })
7099 })
7100 }
7101
7102 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
7103 where
7104 Fn: FnMut(&str) -> String,
7105 {
7106 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7107 let buffer = self.buffer.read(cx).snapshot(cx);
7108
7109 let mut new_selections = Vec::new();
7110 let mut edits = Vec::new();
7111 let mut selection_adjustment = 0i32;
7112
7113 for selection in self.selections.all::<usize>(cx) {
7114 let selection_is_empty = selection.is_empty();
7115
7116 let (start, end) = if selection_is_empty {
7117 let word_range = movement::surrounding_word(
7118 &display_map,
7119 selection.start.to_display_point(&display_map),
7120 );
7121 let start = word_range.start.to_offset(&display_map, Bias::Left);
7122 let end = word_range.end.to_offset(&display_map, Bias::Left);
7123 (start, end)
7124 } else {
7125 (selection.start, selection.end)
7126 };
7127
7128 let text = buffer.text_for_range(start..end).collect::<String>();
7129 let old_length = text.len() as i32;
7130 let text = callback(&text);
7131
7132 new_selections.push(Selection {
7133 start: (start as i32 - selection_adjustment) as usize,
7134 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
7135 goal: SelectionGoal::None,
7136 ..selection
7137 });
7138
7139 selection_adjustment += old_length - text.len() as i32;
7140
7141 edits.push((start..end, text));
7142 }
7143
7144 self.transact(window, cx, |this, window, cx| {
7145 this.buffer.update(cx, |buffer, cx| {
7146 buffer.edit(edits, None, cx);
7147 });
7148
7149 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7150 s.select(new_selections);
7151 });
7152
7153 this.request_autoscroll(Autoscroll::fit(), cx);
7154 });
7155 }
7156
7157 pub fn duplicate(
7158 &mut self,
7159 upwards: bool,
7160 whole_lines: bool,
7161 window: &mut Window,
7162 cx: &mut Context<Self>,
7163 ) {
7164 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7165 let buffer = &display_map.buffer_snapshot;
7166 let selections = self.selections.all::<Point>(cx);
7167
7168 let mut edits = Vec::new();
7169 let mut selections_iter = selections.iter().peekable();
7170 while let Some(selection) = selections_iter.next() {
7171 let mut rows = selection.spanned_rows(false, &display_map);
7172 // duplicate line-wise
7173 if whole_lines || selection.start == selection.end {
7174 // Avoid duplicating the same lines twice.
7175 while let Some(next_selection) = selections_iter.peek() {
7176 let next_rows = next_selection.spanned_rows(false, &display_map);
7177 if next_rows.start < rows.end {
7178 rows.end = next_rows.end;
7179 selections_iter.next().unwrap();
7180 } else {
7181 break;
7182 }
7183 }
7184
7185 // Copy the text from the selected row region and splice it either at the start
7186 // or end of the region.
7187 let start = Point::new(rows.start.0, 0);
7188 let end = Point::new(
7189 rows.end.previous_row().0,
7190 buffer.line_len(rows.end.previous_row()),
7191 );
7192 let text = buffer
7193 .text_for_range(start..end)
7194 .chain(Some("\n"))
7195 .collect::<String>();
7196 let insert_location = if upwards {
7197 Point::new(rows.end.0, 0)
7198 } else {
7199 start
7200 };
7201 edits.push((insert_location..insert_location, text));
7202 } else {
7203 // duplicate character-wise
7204 let start = selection.start;
7205 let end = selection.end;
7206 let text = buffer.text_for_range(start..end).collect::<String>();
7207 edits.push((selection.end..selection.end, text));
7208 }
7209 }
7210
7211 self.transact(window, cx, |this, _, cx| {
7212 this.buffer.update(cx, |buffer, cx| {
7213 buffer.edit(edits, None, cx);
7214 });
7215
7216 this.request_autoscroll(Autoscroll::fit(), cx);
7217 });
7218 }
7219
7220 pub fn duplicate_line_up(
7221 &mut self,
7222 _: &DuplicateLineUp,
7223 window: &mut Window,
7224 cx: &mut Context<Self>,
7225 ) {
7226 self.duplicate(true, true, window, cx);
7227 }
7228
7229 pub fn duplicate_line_down(
7230 &mut self,
7231 _: &DuplicateLineDown,
7232 window: &mut Window,
7233 cx: &mut Context<Self>,
7234 ) {
7235 self.duplicate(false, true, window, cx);
7236 }
7237
7238 pub fn duplicate_selection(
7239 &mut self,
7240 _: &DuplicateSelection,
7241 window: &mut Window,
7242 cx: &mut Context<Self>,
7243 ) {
7244 self.duplicate(false, false, window, cx);
7245 }
7246
7247 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7248 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7249 let buffer = self.buffer.read(cx).snapshot(cx);
7250
7251 let mut edits = Vec::new();
7252 let mut unfold_ranges = Vec::new();
7253 let mut refold_creases = Vec::new();
7254
7255 let selections = self.selections.all::<Point>(cx);
7256 let mut selections = selections.iter().peekable();
7257 let mut contiguous_row_selections = Vec::new();
7258 let mut new_selections = Vec::new();
7259
7260 while let Some(selection) = selections.next() {
7261 // Find all the selections that span a contiguous row range
7262 let (start_row, end_row) = consume_contiguous_rows(
7263 &mut contiguous_row_selections,
7264 selection,
7265 &display_map,
7266 &mut selections,
7267 );
7268
7269 // Move the text spanned by the row range to be before the line preceding the row range
7270 if start_row.0 > 0 {
7271 let range_to_move = Point::new(
7272 start_row.previous_row().0,
7273 buffer.line_len(start_row.previous_row()),
7274 )
7275 ..Point::new(
7276 end_row.previous_row().0,
7277 buffer.line_len(end_row.previous_row()),
7278 );
7279 let insertion_point = display_map
7280 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7281 .0;
7282
7283 // Don't move lines across excerpts
7284 if buffer
7285 .excerpt_containing(insertion_point..range_to_move.end)
7286 .is_some()
7287 {
7288 let text = buffer
7289 .text_for_range(range_to_move.clone())
7290 .flat_map(|s| s.chars())
7291 .skip(1)
7292 .chain(['\n'])
7293 .collect::<String>();
7294
7295 edits.push((
7296 buffer.anchor_after(range_to_move.start)
7297 ..buffer.anchor_before(range_to_move.end),
7298 String::new(),
7299 ));
7300 let insertion_anchor = buffer.anchor_after(insertion_point);
7301 edits.push((insertion_anchor..insertion_anchor, text));
7302
7303 let row_delta = range_to_move.start.row - insertion_point.row + 1;
7304
7305 // Move selections up
7306 new_selections.extend(contiguous_row_selections.drain(..).map(
7307 |mut selection| {
7308 selection.start.row -= row_delta;
7309 selection.end.row -= row_delta;
7310 selection
7311 },
7312 ));
7313
7314 // Move folds up
7315 unfold_ranges.push(range_to_move.clone());
7316 for fold in display_map.folds_in_range(
7317 buffer.anchor_before(range_to_move.start)
7318 ..buffer.anchor_after(range_to_move.end),
7319 ) {
7320 let mut start = fold.range.start.to_point(&buffer);
7321 let mut end = fold.range.end.to_point(&buffer);
7322 start.row -= row_delta;
7323 end.row -= row_delta;
7324 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7325 }
7326 }
7327 }
7328
7329 // If we didn't move line(s), preserve the existing selections
7330 new_selections.append(&mut contiguous_row_selections);
7331 }
7332
7333 self.transact(window, cx, |this, window, cx| {
7334 this.unfold_ranges(&unfold_ranges, true, true, cx);
7335 this.buffer.update(cx, |buffer, cx| {
7336 for (range, text) in edits {
7337 buffer.edit([(range, text)], None, cx);
7338 }
7339 });
7340 this.fold_creases(refold_creases, true, window, cx);
7341 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7342 s.select(new_selections);
7343 })
7344 });
7345 }
7346
7347 pub fn move_line_down(
7348 &mut self,
7349 _: &MoveLineDown,
7350 window: &mut Window,
7351 cx: &mut Context<Self>,
7352 ) {
7353 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7354 let buffer = self.buffer.read(cx).snapshot(cx);
7355
7356 let mut edits = Vec::new();
7357 let mut unfold_ranges = Vec::new();
7358 let mut refold_creases = Vec::new();
7359
7360 let selections = self.selections.all::<Point>(cx);
7361 let mut selections = selections.iter().peekable();
7362 let mut contiguous_row_selections = Vec::new();
7363 let mut new_selections = Vec::new();
7364
7365 while let Some(selection) = selections.next() {
7366 // Find all the selections that span a contiguous row range
7367 let (start_row, end_row) = consume_contiguous_rows(
7368 &mut contiguous_row_selections,
7369 selection,
7370 &display_map,
7371 &mut selections,
7372 );
7373
7374 // Move the text spanned by the row range to be after the last line of the row range
7375 if end_row.0 <= buffer.max_point().row {
7376 let range_to_move =
7377 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7378 let insertion_point = display_map
7379 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7380 .0;
7381
7382 // Don't move lines across excerpt boundaries
7383 if buffer
7384 .excerpt_containing(range_to_move.start..insertion_point)
7385 .is_some()
7386 {
7387 let mut text = String::from("\n");
7388 text.extend(buffer.text_for_range(range_to_move.clone()));
7389 text.pop(); // Drop trailing newline
7390 edits.push((
7391 buffer.anchor_after(range_to_move.start)
7392 ..buffer.anchor_before(range_to_move.end),
7393 String::new(),
7394 ));
7395 let insertion_anchor = buffer.anchor_after(insertion_point);
7396 edits.push((insertion_anchor..insertion_anchor, text));
7397
7398 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7399
7400 // Move selections down
7401 new_selections.extend(contiguous_row_selections.drain(..).map(
7402 |mut selection| {
7403 selection.start.row += row_delta;
7404 selection.end.row += row_delta;
7405 selection
7406 },
7407 ));
7408
7409 // Move folds down
7410 unfold_ranges.push(range_to_move.clone());
7411 for fold in display_map.folds_in_range(
7412 buffer.anchor_before(range_to_move.start)
7413 ..buffer.anchor_after(range_to_move.end),
7414 ) {
7415 let mut start = fold.range.start.to_point(&buffer);
7416 let mut end = fold.range.end.to_point(&buffer);
7417 start.row += row_delta;
7418 end.row += row_delta;
7419 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7420 }
7421 }
7422 }
7423
7424 // If we didn't move line(s), preserve the existing selections
7425 new_selections.append(&mut contiguous_row_selections);
7426 }
7427
7428 self.transact(window, cx, |this, window, cx| {
7429 this.unfold_ranges(&unfold_ranges, true, true, cx);
7430 this.buffer.update(cx, |buffer, cx| {
7431 for (range, text) in edits {
7432 buffer.edit([(range, text)], None, cx);
7433 }
7434 });
7435 this.fold_creases(refold_creases, true, window, cx);
7436 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7437 s.select(new_selections)
7438 });
7439 });
7440 }
7441
7442 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7443 let text_layout_details = &self.text_layout_details(window);
7444 self.transact(window, cx, |this, window, cx| {
7445 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7446 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7447 let line_mode = s.line_mode;
7448 s.move_with(|display_map, selection| {
7449 if !selection.is_empty() || line_mode {
7450 return;
7451 }
7452
7453 let mut head = selection.head();
7454 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7455 if head.column() == display_map.line_len(head.row()) {
7456 transpose_offset = display_map
7457 .buffer_snapshot
7458 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7459 }
7460
7461 if transpose_offset == 0 {
7462 return;
7463 }
7464
7465 *head.column_mut() += 1;
7466 head = display_map.clip_point(head, Bias::Right);
7467 let goal = SelectionGoal::HorizontalPosition(
7468 display_map
7469 .x_for_display_point(head, text_layout_details)
7470 .into(),
7471 );
7472 selection.collapse_to(head, goal);
7473
7474 let transpose_start = display_map
7475 .buffer_snapshot
7476 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7477 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7478 let transpose_end = display_map
7479 .buffer_snapshot
7480 .clip_offset(transpose_offset + 1, Bias::Right);
7481 if let Some(ch) =
7482 display_map.buffer_snapshot.chars_at(transpose_start).next()
7483 {
7484 edits.push((transpose_start..transpose_offset, String::new()));
7485 edits.push((transpose_end..transpose_end, ch.to_string()));
7486 }
7487 }
7488 });
7489 edits
7490 });
7491 this.buffer
7492 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7493 let selections = this.selections.all::<usize>(cx);
7494 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7495 s.select(selections);
7496 });
7497 });
7498 }
7499
7500 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7501 self.rewrap_impl(IsVimMode::No, cx)
7502 }
7503
7504 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7505 let buffer = self.buffer.read(cx).snapshot(cx);
7506 let selections = self.selections.all::<Point>(cx);
7507 let mut selections = selections.iter().peekable();
7508
7509 let mut edits = Vec::new();
7510 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7511
7512 while let Some(selection) = selections.next() {
7513 let mut start_row = selection.start.row;
7514 let mut end_row = selection.end.row;
7515
7516 // Skip selections that overlap with a range that has already been rewrapped.
7517 let selection_range = start_row..end_row;
7518 if rewrapped_row_ranges
7519 .iter()
7520 .any(|range| range.overlaps(&selection_range))
7521 {
7522 continue;
7523 }
7524
7525 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
7526
7527 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7528 match language_scope.language_name().as_ref() {
7529 "Markdown" | "Plain Text" => {
7530 should_rewrap = true;
7531 }
7532 _ => {}
7533 }
7534 }
7535
7536 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7537
7538 // Since not all lines in the selection may be at the same indent
7539 // level, choose the indent size that is the most common between all
7540 // of the lines.
7541 //
7542 // If there is a tie, we use the deepest indent.
7543 let (indent_size, indent_end) = {
7544 let mut indent_size_occurrences = HashMap::default();
7545 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7546
7547 for row in start_row..=end_row {
7548 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7549 rows_by_indent_size.entry(indent).or_default().push(row);
7550 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7551 }
7552
7553 let indent_size = indent_size_occurrences
7554 .into_iter()
7555 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7556 .map(|(indent, _)| indent)
7557 .unwrap_or_default();
7558 let row = rows_by_indent_size[&indent_size][0];
7559 let indent_end = Point::new(row, indent_size.len);
7560
7561 (indent_size, indent_end)
7562 };
7563
7564 let mut line_prefix = indent_size.chars().collect::<String>();
7565
7566 if let Some(comment_prefix) =
7567 buffer
7568 .language_scope_at(selection.head())
7569 .and_then(|language| {
7570 language
7571 .line_comment_prefixes()
7572 .iter()
7573 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7574 .cloned()
7575 })
7576 {
7577 line_prefix.push_str(&comment_prefix);
7578 should_rewrap = true;
7579 }
7580
7581 if !should_rewrap {
7582 continue;
7583 }
7584
7585 if selection.is_empty() {
7586 'expand_upwards: while start_row > 0 {
7587 let prev_row = start_row - 1;
7588 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7589 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7590 {
7591 start_row = prev_row;
7592 } else {
7593 break 'expand_upwards;
7594 }
7595 }
7596
7597 'expand_downwards: while end_row < buffer.max_point().row {
7598 let next_row = end_row + 1;
7599 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7600 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7601 {
7602 end_row = next_row;
7603 } else {
7604 break 'expand_downwards;
7605 }
7606 }
7607 }
7608
7609 let start = Point::new(start_row, 0);
7610 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7611 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7612 let Some(lines_without_prefixes) = selection_text
7613 .lines()
7614 .map(|line| {
7615 line.strip_prefix(&line_prefix)
7616 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7617 .ok_or_else(|| {
7618 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7619 })
7620 })
7621 .collect::<Result<Vec<_>, _>>()
7622 .log_err()
7623 else {
7624 continue;
7625 };
7626
7627 let wrap_column = buffer
7628 .settings_at(Point::new(start_row, 0), cx)
7629 .preferred_line_length as usize;
7630 let wrapped_text = wrap_with_prefix(
7631 line_prefix,
7632 lines_without_prefixes.join(" "),
7633 wrap_column,
7634 tab_size,
7635 );
7636
7637 // TODO: should always use char-based diff while still supporting cursor behavior that
7638 // matches vim.
7639 let diff = match is_vim_mode {
7640 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
7641 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
7642 };
7643 let mut offset = start.to_offset(&buffer);
7644 let mut moved_since_edit = true;
7645
7646 for change in diff.iter_all_changes() {
7647 let value = change.value();
7648 match change.tag() {
7649 ChangeTag::Equal => {
7650 offset += value.len();
7651 moved_since_edit = true;
7652 }
7653 ChangeTag::Delete => {
7654 let start = buffer.anchor_after(offset);
7655 let end = buffer.anchor_before(offset + value.len());
7656
7657 if moved_since_edit {
7658 edits.push((start..end, String::new()));
7659 } else {
7660 edits.last_mut().unwrap().0.end = end;
7661 }
7662
7663 offset += value.len();
7664 moved_since_edit = false;
7665 }
7666 ChangeTag::Insert => {
7667 if moved_since_edit {
7668 let anchor = buffer.anchor_after(offset);
7669 edits.push((anchor..anchor, value.to_string()));
7670 } else {
7671 edits.last_mut().unwrap().1.push_str(value);
7672 }
7673
7674 moved_since_edit = false;
7675 }
7676 }
7677 }
7678
7679 rewrapped_row_ranges.push(start_row..=end_row);
7680 }
7681
7682 self.buffer
7683 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7684 }
7685
7686 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7687 let mut text = String::new();
7688 let buffer = self.buffer.read(cx).snapshot(cx);
7689 let mut selections = self.selections.all::<Point>(cx);
7690 let mut clipboard_selections = Vec::with_capacity(selections.len());
7691 {
7692 let max_point = buffer.max_point();
7693 let mut is_first = true;
7694 for selection in &mut selections {
7695 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7696 if is_entire_line {
7697 selection.start = Point::new(selection.start.row, 0);
7698 if !selection.is_empty() && selection.end.column == 0 {
7699 selection.end = cmp::min(max_point, selection.end);
7700 } else {
7701 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7702 }
7703 selection.goal = SelectionGoal::None;
7704 }
7705 if is_first {
7706 is_first = false;
7707 } else {
7708 text += "\n";
7709 }
7710 let mut len = 0;
7711 for chunk in buffer.text_for_range(selection.start..selection.end) {
7712 text.push_str(chunk);
7713 len += chunk.len();
7714 }
7715 clipboard_selections.push(ClipboardSelection {
7716 len,
7717 is_entire_line,
7718 first_line_indent: buffer
7719 .indent_size_for_line(MultiBufferRow(selection.start.row))
7720 .len,
7721 });
7722 }
7723 }
7724
7725 self.transact(window, cx, |this, window, cx| {
7726 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7727 s.select(selections);
7728 });
7729 this.insert("", window, cx);
7730 });
7731 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7732 }
7733
7734 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
7735 let item = self.cut_common(window, cx);
7736 cx.write_to_clipboard(item);
7737 }
7738
7739 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
7740 self.change_selections(None, window, cx, |s| {
7741 s.move_with(|snapshot, sel| {
7742 if sel.is_empty() {
7743 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7744 }
7745 });
7746 });
7747 let item = self.cut_common(window, cx);
7748 cx.set_global(KillRing(item))
7749 }
7750
7751 pub fn kill_ring_yank(
7752 &mut self,
7753 _: &KillRingYank,
7754 window: &mut Window,
7755 cx: &mut Context<Self>,
7756 ) {
7757 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7758 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7759 (kill_ring.text().to_string(), kill_ring.metadata_json())
7760 } else {
7761 return;
7762 }
7763 } else {
7764 return;
7765 };
7766 self.do_paste(&text, metadata, false, window, cx);
7767 }
7768
7769 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
7770 let selections = self.selections.all::<Point>(cx);
7771 let buffer = self.buffer.read(cx).read(cx);
7772 let mut text = String::new();
7773
7774 let mut clipboard_selections = Vec::with_capacity(selections.len());
7775 {
7776 let max_point = buffer.max_point();
7777 let mut is_first = true;
7778 for selection in selections.iter() {
7779 let mut start = selection.start;
7780 let mut end = selection.end;
7781 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7782 if is_entire_line {
7783 start = Point::new(start.row, 0);
7784 end = cmp::min(max_point, Point::new(end.row + 1, 0));
7785 }
7786 if is_first {
7787 is_first = false;
7788 } else {
7789 text += "\n";
7790 }
7791 let mut len = 0;
7792 for chunk in buffer.text_for_range(start..end) {
7793 text.push_str(chunk);
7794 len += chunk.len();
7795 }
7796 clipboard_selections.push(ClipboardSelection {
7797 len,
7798 is_entire_line,
7799 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
7800 });
7801 }
7802 }
7803
7804 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7805 text,
7806 clipboard_selections,
7807 ));
7808 }
7809
7810 pub fn do_paste(
7811 &mut self,
7812 text: &String,
7813 clipboard_selections: Option<Vec<ClipboardSelection>>,
7814 handle_entire_lines: bool,
7815 window: &mut Window,
7816 cx: &mut Context<Self>,
7817 ) {
7818 if self.read_only(cx) {
7819 return;
7820 }
7821
7822 let clipboard_text = Cow::Borrowed(text);
7823
7824 self.transact(window, cx, |this, window, cx| {
7825 if let Some(mut clipboard_selections) = clipboard_selections {
7826 let old_selections = this.selections.all::<usize>(cx);
7827 let all_selections_were_entire_line =
7828 clipboard_selections.iter().all(|s| s.is_entire_line);
7829 let first_selection_indent_column =
7830 clipboard_selections.first().map(|s| s.first_line_indent);
7831 if clipboard_selections.len() != old_selections.len() {
7832 clipboard_selections.drain(..);
7833 }
7834 let cursor_offset = this.selections.last::<usize>(cx).head();
7835 let mut auto_indent_on_paste = true;
7836
7837 this.buffer.update(cx, |buffer, cx| {
7838 let snapshot = buffer.read(cx);
7839 auto_indent_on_paste =
7840 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7841
7842 let mut start_offset = 0;
7843 let mut edits = Vec::new();
7844 let mut original_indent_columns = Vec::new();
7845 for (ix, selection) in old_selections.iter().enumerate() {
7846 let to_insert;
7847 let entire_line;
7848 let original_indent_column;
7849 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7850 let end_offset = start_offset + clipboard_selection.len;
7851 to_insert = &clipboard_text[start_offset..end_offset];
7852 entire_line = clipboard_selection.is_entire_line;
7853 start_offset = end_offset + 1;
7854 original_indent_column = Some(clipboard_selection.first_line_indent);
7855 } else {
7856 to_insert = clipboard_text.as_str();
7857 entire_line = all_selections_were_entire_line;
7858 original_indent_column = first_selection_indent_column
7859 }
7860
7861 // If the corresponding selection was empty when this slice of the
7862 // clipboard text was written, then the entire line containing the
7863 // selection was copied. If this selection is also currently empty,
7864 // then paste the line before the current line of the buffer.
7865 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7866 let column = selection.start.to_point(&snapshot).column as usize;
7867 let line_start = selection.start - column;
7868 line_start..line_start
7869 } else {
7870 selection.range()
7871 };
7872
7873 edits.push((range, to_insert));
7874 original_indent_columns.extend(original_indent_column);
7875 }
7876 drop(snapshot);
7877
7878 buffer.edit(
7879 edits,
7880 if auto_indent_on_paste {
7881 Some(AutoindentMode::Block {
7882 original_indent_columns,
7883 })
7884 } else {
7885 None
7886 },
7887 cx,
7888 );
7889 });
7890
7891 let selections = this.selections.all::<usize>(cx);
7892 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7893 s.select(selections)
7894 });
7895 } else {
7896 this.insert(&clipboard_text, window, cx);
7897 }
7898 });
7899 }
7900
7901 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
7902 if let Some(item) = cx.read_from_clipboard() {
7903 let entries = item.entries();
7904
7905 match entries.first() {
7906 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7907 // of all the pasted entries.
7908 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7909 .do_paste(
7910 clipboard_string.text(),
7911 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7912 true,
7913 window,
7914 cx,
7915 ),
7916 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
7917 }
7918 }
7919 }
7920
7921 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
7922 if self.read_only(cx) {
7923 return;
7924 }
7925
7926 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7927 if let Some((selections, _)) =
7928 self.selection_history.transaction(transaction_id).cloned()
7929 {
7930 self.change_selections(None, window, cx, |s| {
7931 s.select_anchors(selections.to_vec());
7932 });
7933 }
7934 self.request_autoscroll(Autoscroll::fit(), cx);
7935 self.unmark_text(window, cx);
7936 self.refresh_inline_completion(true, false, window, cx);
7937 cx.emit(EditorEvent::Edited { transaction_id });
7938 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7939 }
7940 }
7941
7942 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
7943 if self.read_only(cx) {
7944 return;
7945 }
7946
7947 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7948 if let Some((_, Some(selections))) =
7949 self.selection_history.transaction(transaction_id).cloned()
7950 {
7951 self.change_selections(None, window, cx, |s| {
7952 s.select_anchors(selections.to_vec());
7953 });
7954 }
7955 self.request_autoscroll(Autoscroll::fit(), cx);
7956 self.unmark_text(window, cx);
7957 self.refresh_inline_completion(true, false, window, cx);
7958 cx.emit(EditorEvent::Edited { transaction_id });
7959 }
7960 }
7961
7962 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
7963 self.buffer
7964 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7965 }
7966
7967 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
7968 self.buffer
7969 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7970 }
7971
7972 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
7973 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7974 let line_mode = s.line_mode;
7975 s.move_with(|map, selection| {
7976 let cursor = if selection.is_empty() && !line_mode {
7977 movement::left(map, selection.start)
7978 } else {
7979 selection.start
7980 };
7981 selection.collapse_to(cursor, SelectionGoal::None);
7982 });
7983 })
7984 }
7985
7986 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
7987 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7988 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7989 })
7990 }
7991
7992 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
7993 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7994 let line_mode = s.line_mode;
7995 s.move_with(|map, selection| {
7996 let cursor = if selection.is_empty() && !line_mode {
7997 movement::right(map, selection.end)
7998 } else {
7999 selection.end
8000 };
8001 selection.collapse_to(cursor, SelectionGoal::None)
8002 });
8003 })
8004 }
8005
8006 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
8007 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8008 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
8009 })
8010 }
8011
8012 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
8013 if self.take_rename(true, window, cx).is_some() {
8014 return;
8015 }
8016
8017 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8018 cx.propagate();
8019 return;
8020 }
8021
8022 let text_layout_details = &self.text_layout_details(window);
8023 let selection_count = self.selections.count();
8024 let first_selection = self.selections.first_anchor();
8025
8026 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8027 let line_mode = s.line_mode;
8028 s.move_with(|map, selection| {
8029 if !selection.is_empty() && !line_mode {
8030 selection.goal = SelectionGoal::None;
8031 }
8032 let (cursor, goal) = movement::up(
8033 map,
8034 selection.start,
8035 selection.goal,
8036 false,
8037 text_layout_details,
8038 );
8039 selection.collapse_to(cursor, goal);
8040 });
8041 });
8042
8043 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8044 {
8045 cx.propagate();
8046 }
8047 }
8048
8049 pub fn move_up_by_lines(
8050 &mut self,
8051 action: &MoveUpByLines,
8052 window: &mut Window,
8053 cx: &mut Context<Self>,
8054 ) {
8055 if self.take_rename(true, window, cx).is_some() {
8056 return;
8057 }
8058
8059 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8060 cx.propagate();
8061 return;
8062 }
8063
8064 let text_layout_details = &self.text_layout_details(window);
8065
8066 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8067 let line_mode = s.line_mode;
8068 s.move_with(|map, selection| {
8069 if !selection.is_empty() && !line_mode {
8070 selection.goal = SelectionGoal::None;
8071 }
8072 let (cursor, goal) = movement::up_by_rows(
8073 map,
8074 selection.start,
8075 action.lines,
8076 selection.goal,
8077 false,
8078 text_layout_details,
8079 );
8080 selection.collapse_to(cursor, goal);
8081 });
8082 })
8083 }
8084
8085 pub fn move_down_by_lines(
8086 &mut self,
8087 action: &MoveDownByLines,
8088 window: &mut Window,
8089 cx: &mut Context<Self>,
8090 ) {
8091 if self.take_rename(true, window, cx).is_some() {
8092 return;
8093 }
8094
8095 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8096 cx.propagate();
8097 return;
8098 }
8099
8100 let text_layout_details = &self.text_layout_details(window);
8101
8102 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8103 let line_mode = s.line_mode;
8104 s.move_with(|map, selection| {
8105 if !selection.is_empty() && !line_mode {
8106 selection.goal = SelectionGoal::None;
8107 }
8108 let (cursor, goal) = movement::down_by_rows(
8109 map,
8110 selection.start,
8111 action.lines,
8112 selection.goal,
8113 false,
8114 text_layout_details,
8115 );
8116 selection.collapse_to(cursor, goal);
8117 });
8118 })
8119 }
8120
8121 pub fn select_down_by_lines(
8122 &mut self,
8123 action: &SelectDownByLines,
8124 window: &mut Window,
8125 cx: &mut Context<Self>,
8126 ) {
8127 let text_layout_details = &self.text_layout_details(window);
8128 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8129 s.move_heads_with(|map, head, goal| {
8130 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
8131 })
8132 })
8133 }
8134
8135 pub fn select_up_by_lines(
8136 &mut self,
8137 action: &SelectUpByLines,
8138 window: &mut Window,
8139 cx: &mut Context<Self>,
8140 ) {
8141 let text_layout_details = &self.text_layout_details(window);
8142 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8143 s.move_heads_with(|map, head, goal| {
8144 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
8145 })
8146 })
8147 }
8148
8149 pub fn select_page_up(
8150 &mut self,
8151 _: &SelectPageUp,
8152 window: &mut Window,
8153 cx: &mut Context<Self>,
8154 ) {
8155 let Some(row_count) = self.visible_row_count() else {
8156 return;
8157 };
8158
8159 let text_layout_details = &self.text_layout_details(window);
8160
8161 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8162 s.move_heads_with(|map, head, goal| {
8163 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
8164 })
8165 })
8166 }
8167
8168 pub fn move_page_up(
8169 &mut self,
8170 action: &MovePageUp,
8171 window: &mut Window,
8172 cx: &mut Context<Self>,
8173 ) {
8174 if self.take_rename(true, window, cx).is_some() {
8175 return;
8176 }
8177
8178 if self
8179 .context_menu
8180 .borrow_mut()
8181 .as_mut()
8182 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
8183 .unwrap_or(false)
8184 {
8185 return;
8186 }
8187
8188 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8189 cx.propagate();
8190 return;
8191 }
8192
8193 let Some(row_count) = self.visible_row_count() else {
8194 return;
8195 };
8196
8197 let autoscroll = if action.center_cursor {
8198 Autoscroll::center()
8199 } else {
8200 Autoscroll::fit()
8201 };
8202
8203 let text_layout_details = &self.text_layout_details(window);
8204
8205 self.change_selections(Some(autoscroll), window, cx, |s| {
8206 let line_mode = s.line_mode;
8207 s.move_with(|map, selection| {
8208 if !selection.is_empty() && !line_mode {
8209 selection.goal = SelectionGoal::None;
8210 }
8211 let (cursor, goal) = movement::up_by_rows(
8212 map,
8213 selection.end,
8214 row_count,
8215 selection.goal,
8216 false,
8217 text_layout_details,
8218 );
8219 selection.collapse_to(cursor, goal);
8220 });
8221 });
8222 }
8223
8224 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
8225 let text_layout_details = &self.text_layout_details(window);
8226 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8227 s.move_heads_with(|map, head, goal| {
8228 movement::up(map, head, goal, false, text_layout_details)
8229 })
8230 })
8231 }
8232
8233 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
8234 self.take_rename(true, window, cx);
8235
8236 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8237 cx.propagate();
8238 return;
8239 }
8240
8241 let text_layout_details = &self.text_layout_details(window);
8242 let selection_count = self.selections.count();
8243 let first_selection = self.selections.first_anchor();
8244
8245 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8246 let line_mode = s.line_mode;
8247 s.move_with(|map, selection| {
8248 if !selection.is_empty() && !line_mode {
8249 selection.goal = SelectionGoal::None;
8250 }
8251 let (cursor, goal) = movement::down(
8252 map,
8253 selection.end,
8254 selection.goal,
8255 false,
8256 text_layout_details,
8257 );
8258 selection.collapse_to(cursor, goal);
8259 });
8260 });
8261
8262 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8263 {
8264 cx.propagate();
8265 }
8266 }
8267
8268 pub fn select_page_down(
8269 &mut self,
8270 _: &SelectPageDown,
8271 window: &mut Window,
8272 cx: &mut Context<Self>,
8273 ) {
8274 let Some(row_count) = self.visible_row_count() else {
8275 return;
8276 };
8277
8278 let text_layout_details = &self.text_layout_details(window);
8279
8280 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8281 s.move_heads_with(|map, head, goal| {
8282 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
8283 })
8284 })
8285 }
8286
8287 pub fn move_page_down(
8288 &mut self,
8289 action: &MovePageDown,
8290 window: &mut Window,
8291 cx: &mut Context<Self>,
8292 ) {
8293 if self.take_rename(true, window, cx).is_some() {
8294 return;
8295 }
8296
8297 if self
8298 .context_menu
8299 .borrow_mut()
8300 .as_mut()
8301 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
8302 .unwrap_or(false)
8303 {
8304 return;
8305 }
8306
8307 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8308 cx.propagate();
8309 return;
8310 }
8311
8312 let Some(row_count) = self.visible_row_count() else {
8313 return;
8314 };
8315
8316 let autoscroll = if action.center_cursor {
8317 Autoscroll::center()
8318 } else {
8319 Autoscroll::fit()
8320 };
8321
8322 let text_layout_details = &self.text_layout_details(window);
8323 self.change_selections(Some(autoscroll), window, cx, |s| {
8324 let line_mode = s.line_mode;
8325 s.move_with(|map, selection| {
8326 if !selection.is_empty() && !line_mode {
8327 selection.goal = SelectionGoal::None;
8328 }
8329 let (cursor, goal) = movement::down_by_rows(
8330 map,
8331 selection.end,
8332 row_count,
8333 selection.goal,
8334 false,
8335 text_layout_details,
8336 );
8337 selection.collapse_to(cursor, goal);
8338 });
8339 });
8340 }
8341
8342 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
8343 let text_layout_details = &self.text_layout_details(window);
8344 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8345 s.move_heads_with(|map, head, goal| {
8346 movement::down(map, head, goal, false, text_layout_details)
8347 })
8348 });
8349 }
8350
8351 pub fn context_menu_first(
8352 &mut self,
8353 _: &ContextMenuFirst,
8354 _window: &mut Window,
8355 cx: &mut Context<Self>,
8356 ) {
8357 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8358 context_menu.select_first(self.completion_provider.as_deref(), cx);
8359 }
8360 }
8361
8362 pub fn context_menu_prev(
8363 &mut self,
8364 _: &ContextMenuPrev,
8365 _window: &mut Window,
8366 cx: &mut Context<Self>,
8367 ) {
8368 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8369 context_menu.select_prev(self.completion_provider.as_deref(), cx);
8370 }
8371 }
8372
8373 pub fn context_menu_next(
8374 &mut self,
8375 _: &ContextMenuNext,
8376 _window: &mut Window,
8377 cx: &mut Context<Self>,
8378 ) {
8379 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8380 context_menu.select_next(self.completion_provider.as_deref(), cx);
8381 }
8382 }
8383
8384 pub fn context_menu_last(
8385 &mut self,
8386 _: &ContextMenuLast,
8387 _window: &mut Window,
8388 cx: &mut Context<Self>,
8389 ) {
8390 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8391 context_menu.select_last(self.completion_provider.as_deref(), cx);
8392 }
8393 }
8394
8395 pub fn move_to_previous_word_start(
8396 &mut self,
8397 _: &MoveToPreviousWordStart,
8398 window: &mut Window,
8399 cx: &mut Context<Self>,
8400 ) {
8401 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8402 s.move_cursors_with(|map, head, _| {
8403 (
8404 movement::previous_word_start(map, head),
8405 SelectionGoal::None,
8406 )
8407 });
8408 })
8409 }
8410
8411 pub fn move_to_previous_subword_start(
8412 &mut self,
8413 _: &MoveToPreviousSubwordStart,
8414 window: &mut Window,
8415 cx: &mut Context<Self>,
8416 ) {
8417 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8418 s.move_cursors_with(|map, head, _| {
8419 (
8420 movement::previous_subword_start(map, head),
8421 SelectionGoal::None,
8422 )
8423 });
8424 })
8425 }
8426
8427 pub fn select_to_previous_word_start(
8428 &mut self,
8429 _: &SelectToPreviousWordStart,
8430 window: &mut Window,
8431 cx: &mut Context<Self>,
8432 ) {
8433 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8434 s.move_heads_with(|map, head, _| {
8435 (
8436 movement::previous_word_start(map, head),
8437 SelectionGoal::None,
8438 )
8439 });
8440 })
8441 }
8442
8443 pub fn select_to_previous_subword_start(
8444 &mut self,
8445 _: &SelectToPreviousSubwordStart,
8446 window: &mut Window,
8447 cx: &mut Context<Self>,
8448 ) {
8449 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8450 s.move_heads_with(|map, head, _| {
8451 (
8452 movement::previous_subword_start(map, head),
8453 SelectionGoal::None,
8454 )
8455 });
8456 })
8457 }
8458
8459 pub fn delete_to_previous_word_start(
8460 &mut self,
8461 action: &DeleteToPreviousWordStart,
8462 window: &mut Window,
8463 cx: &mut Context<Self>,
8464 ) {
8465 self.transact(window, cx, |this, window, cx| {
8466 this.select_autoclose_pair(window, cx);
8467 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8468 let line_mode = s.line_mode;
8469 s.move_with(|map, selection| {
8470 if selection.is_empty() && !line_mode {
8471 let cursor = if action.ignore_newlines {
8472 movement::previous_word_start(map, selection.head())
8473 } else {
8474 movement::previous_word_start_or_newline(map, selection.head())
8475 };
8476 selection.set_head(cursor, SelectionGoal::None);
8477 }
8478 });
8479 });
8480 this.insert("", window, cx);
8481 });
8482 }
8483
8484 pub fn delete_to_previous_subword_start(
8485 &mut self,
8486 _: &DeleteToPreviousSubwordStart,
8487 window: &mut Window,
8488 cx: &mut Context<Self>,
8489 ) {
8490 self.transact(window, cx, |this, window, cx| {
8491 this.select_autoclose_pair(window, cx);
8492 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8493 let line_mode = s.line_mode;
8494 s.move_with(|map, selection| {
8495 if selection.is_empty() && !line_mode {
8496 let cursor = movement::previous_subword_start(map, selection.head());
8497 selection.set_head(cursor, SelectionGoal::None);
8498 }
8499 });
8500 });
8501 this.insert("", window, cx);
8502 });
8503 }
8504
8505 pub fn move_to_next_word_end(
8506 &mut self,
8507 _: &MoveToNextWordEnd,
8508 window: &mut Window,
8509 cx: &mut Context<Self>,
8510 ) {
8511 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8512 s.move_cursors_with(|map, head, _| {
8513 (movement::next_word_end(map, head), SelectionGoal::None)
8514 });
8515 })
8516 }
8517
8518 pub fn move_to_next_subword_end(
8519 &mut self,
8520 _: &MoveToNextSubwordEnd,
8521 window: &mut Window,
8522 cx: &mut Context<Self>,
8523 ) {
8524 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8525 s.move_cursors_with(|map, head, _| {
8526 (movement::next_subword_end(map, head), SelectionGoal::None)
8527 });
8528 })
8529 }
8530
8531 pub fn select_to_next_word_end(
8532 &mut self,
8533 _: &SelectToNextWordEnd,
8534 window: &mut Window,
8535 cx: &mut Context<Self>,
8536 ) {
8537 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8538 s.move_heads_with(|map, head, _| {
8539 (movement::next_word_end(map, head), SelectionGoal::None)
8540 });
8541 })
8542 }
8543
8544 pub fn select_to_next_subword_end(
8545 &mut self,
8546 _: &SelectToNextSubwordEnd,
8547 window: &mut Window,
8548 cx: &mut Context<Self>,
8549 ) {
8550 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8551 s.move_heads_with(|map, head, _| {
8552 (movement::next_subword_end(map, head), SelectionGoal::None)
8553 });
8554 })
8555 }
8556
8557 pub fn delete_to_next_word_end(
8558 &mut self,
8559 action: &DeleteToNextWordEnd,
8560 window: &mut Window,
8561 cx: &mut Context<Self>,
8562 ) {
8563 self.transact(window, cx, |this, window, cx| {
8564 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8565 let line_mode = s.line_mode;
8566 s.move_with(|map, selection| {
8567 if selection.is_empty() && !line_mode {
8568 let cursor = if action.ignore_newlines {
8569 movement::next_word_end(map, selection.head())
8570 } else {
8571 movement::next_word_end_or_newline(map, selection.head())
8572 };
8573 selection.set_head(cursor, SelectionGoal::None);
8574 }
8575 });
8576 });
8577 this.insert("", window, cx);
8578 });
8579 }
8580
8581 pub fn delete_to_next_subword_end(
8582 &mut self,
8583 _: &DeleteToNextSubwordEnd,
8584 window: &mut Window,
8585 cx: &mut Context<Self>,
8586 ) {
8587 self.transact(window, cx, |this, window, cx| {
8588 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8589 s.move_with(|map, selection| {
8590 if selection.is_empty() {
8591 let cursor = movement::next_subword_end(map, selection.head());
8592 selection.set_head(cursor, SelectionGoal::None);
8593 }
8594 });
8595 });
8596 this.insert("", window, cx);
8597 });
8598 }
8599
8600 pub fn move_to_beginning_of_line(
8601 &mut self,
8602 action: &MoveToBeginningOfLine,
8603 window: &mut Window,
8604 cx: &mut Context<Self>,
8605 ) {
8606 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8607 s.move_cursors_with(|map, head, _| {
8608 (
8609 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8610 SelectionGoal::None,
8611 )
8612 });
8613 })
8614 }
8615
8616 pub fn select_to_beginning_of_line(
8617 &mut self,
8618 action: &SelectToBeginningOfLine,
8619 window: &mut Window,
8620 cx: &mut Context<Self>,
8621 ) {
8622 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8623 s.move_heads_with(|map, head, _| {
8624 (
8625 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8626 SelectionGoal::None,
8627 )
8628 });
8629 });
8630 }
8631
8632 pub fn delete_to_beginning_of_line(
8633 &mut self,
8634 _: &DeleteToBeginningOfLine,
8635 window: &mut Window,
8636 cx: &mut Context<Self>,
8637 ) {
8638 self.transact(window, cx, |this, window, cx| {
8639 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8640 s.move_with(|_, selection| {
8641 selection.reversed = true;
8642 });
8643 });
8644
8645 this.select_to_beginning_of_line(
8646 &SelectToBeginningOfLine {
8647 stop_at_soft_wraps: false,
8648 },
8649 window,
8650 cx,
8651 );
8652 this.backspace(&Backspace, window, cx);
8653 });
8654 }
8655
8656 pub fn move_to_end_of_line(
8657 &mut self,
8658 action: &MoveToEndOfLine,
8659 window: &mut Window,
8660 cx: &mut Context<Self>,
8661 ) {
8662 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8663 s.move_cursors_with(|map, head, _| {
8664 (
8665 movement::line_end(map, head, action.stop_at_soft_wraps),
8666 SelectionGoal::None,
8667 )
8668 });
8669 })
8670 }
8671
8672 pub fn select_to_end_of_line(
8673 &mut self,
8674 action: &SelectToEndOfLine,
8675 window: &mut Window,
8676 cx: &mut Context<Self>,
8677 ) {
8678 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8679 s.move_heads_with(|map, head, _| {
8680 (
8681 movement::line_end(map, head, action.stop_at_soft_wraps),
8682 SelectionGoal::None,
8683 )
8684 });
8685 })
8686 }
8687
8688 pub fn delete_to_end_of_line(
8689 &mut self,
8690 _: &DeleteToEndOfLine,
8691 window: &mut Window,
8692 cx: &mut Context<Self>,
8693 ) {
8694 self.transact(window, cx, |this, window, cx| {
8695 this.select_to_end_of_line(
8696 &SelectToEndOfLine {
8697 stop_at_soft_wraps: false,
8698 },
8699 window,
8700 cx,
8701 );
8702 this.delete(&Delete, window, cx);
8703 });
8704 }
8705
8706 pub fn cut_to_end_of_line(
8707 &mut self,
8708 _: &CutToEndOfLine,
8709 window: &mut Window,
8710 cx: &mut Context<Self>,
8711 ) {
8712 self.transact(window, cx, |this, window, cx| {
8713 this.select_to_end_of_line(
8714 &SelectToEndOfLine {
8715 stop_at_soft_wraps: false,
8716 },
8717 window,
8718 cx,
8719 );
8720 this.cut(&Cut, window, cx);
8721 });
8722 }
8723
8724 pub fn move_to_start_of_paragraph(
8725 &mut self,
8726 _: &MoveToStartOfParagraph,
8727 window: &mut Window,
8728 cx: &mut Context<Self>,
8729 ) {
8730 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8731 cx.propagate();
8732 return;
8733 }
8734
8735 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8736 s.move_with(|map, selection| {
8737 selection.collapse_to(
8738 movement::start_of_paragraph(map, selection.head(), 1),
8739 SelectionGoal::None,
8740 )
8741 });
8742 })
8743 }
8744
8745 pub fn move_to_end_of_paragraph(
8746 &mut self,
8747 _: &MoveToEndOfParagraph,
8748 window: &mut Window,
8749 cx: &mut Context<Self>,
8750 ) {
8751 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8752 cx.propagate();
8753 return;
8754 }
8755
8756 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8757 s.move_with(|map, selection| {
8758 selection.collapse_to(
8759 movement::end_of_paragraph(map, selection.head(), 1),
8760 SelectionGoal::None,
8761 )
8762 });
8763 })
8764 }
8765
8766 pub fn select_to_start_of_paragraph(
8767 &mut self,
8768 _: &SelectToStartOfParagraph,
8769 window: &mut Window,
8770 cx: &mut Context<Self>,
8771 ) {
8772 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8773 cx.propagate();
8774 return;
8775 }
8776
8777 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8778 s.move_heads_with(|map, head, _| {
8779 (
8780 movement::start_of_paragraph(map, head, 1),
8781 SelectionGoal::None,
8782 )
8783 });
8784 })
8785 }
8786
8787 pub fn select_to_end_of_paragraph(
8788 &mut self,
8789 _: &SelectToEndOfParagraph,
8790 window: &mut Window,
8791 cx: &mut Context<Self>,
8792 ) {
8793 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8794 cx.propagate();
8795 return;
8796 }
8797
8798 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8799 s.move_heads_with(|map, head, _| {
8800 (
8801 movement::end_of_paragraph(map, head, 1),
8802 SelectionGoal::None,
8803 )
8804 });
8805 })
8806 }
8807
8808 pub fn move_to_beginning(
8809 &mut self,
8810 _: &MoveToBeginning,
8811 window: &mut Window,
8812 cx: &mut Context<Self>,
8813 ) {
8814 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8815 cx.propagate();
8816 return;
8817 }
8818
8819 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8820 s.select_ranges(vec![0..0]);
8821 });
8822 }
8823
8824 pub fn select_to_beginning(
8825 &mut self,
8826 _: &SelectToBeginning,
8827 window: &mut Window,
8828 cx: &mut Context<Self>,
8829 ) {
8830 let mut selection = self.selections.last::<Point>(cx);
8831 selection.set_head(Point::zero(), SelectionGoal::None);
8832
8833 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8834 s.select(vec![selection]);
8835 });
8836 }
8837
8838 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
8839 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8840 cx.propagate();
8841 return;
8842 }
8843
8844 let cursor = self.buffer.read(cx).read(cx).len();
8845 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8846 s.select_ranges(vec![cursor..cursor])
8847 });
8848 }
8849
8850 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
8851 self.nav_history = nav_history;
8852 }
8853
8854 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
8855 self.nav_history.as_ref()
8856 }
8857
8858 fn push_to_nav_history(
8859 &mut self,
8860 cursor_anchor: Anchor,
8861 new_position: Option<Point>,
8862 cx: &mut Context<Self>,
8863 ) {
8864 if let Some(nav_history) = self.nav_history.as_mut() {
8865 let buffer = self.buffer.read(cx).read(cx);
8866 let cursor_position = cursor_anchor.to_point(&buffer);
8867 let scroll_state = self.scroll_manager.anchor();
8868 let scroll_top_row = scroll_state.top_row(&buffer);
8869 drop(buffer);
8870
8871 if let Some(new_position) = new_position {
8872 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
8873 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
8874 return;
8875 }
8876 }
8877
8878 nav_history.push(
8879 Some(NavigationData {
8880 cursor_anchor,
8881 cursor_position,
8882 scroll_anchor: scroll_state,
8883 scroll_top_row,
8884 }),
8885 cx,
8886 );
8887 }
8888 }
8889
8890 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
8891 let buffer = self.buffer.read(cx).snapshot(cx);
8892 let mut selection = self.selections.first::<usize>(cx);
8893 selection.set_head(buffer.len(), SelectionGoal::None);
8894 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8895 s.select(vec![selection]);
8896 });
8897 }
8898
8899 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
8900 let end = self.buffer.read(cx).read(cx).len();
8901 self.change_selections(None, window, cx, |s| {
8902 s.select_ranges(vec![0..end]);
8903 });
8904 }
8905
8906 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
8907 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8908 let mut selections = self.selections.all::<Point>(cx);
8909 let max_point = display_map.buffer_snapshot.max_point();
8910 for selection in &mut selections {
8911 let rows = selection.spanned_rows(true, &display_map);
8912 selection.start = Point::new(rows.start.0, 0);
8913 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
8914 selection.reversed = false;
8915 }
8916 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8917 s.select(selections);
8918 });
8919 }
8920
8921 pub fn split_selection_into_lines(
8922 &mut self,
8923 _: &SplitSelectionIntoLines,
8924 window: &mut Window,
8925 cx: &mut Context<Self>,
8926 ) {
8927 let mut to_unfold = Vec::new();
8928 let mut new_selection_ranges = Vec::new();
8929 {
8930 let selections = self.selections.all::<Point>(cx);
8931 let buffer = self.buffer.read(cx).read(cx);
8932 for selection in selections {
8933 for row in selection.start.row..selection.end.row {
8934 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8935 new_selection_ranges.push(cursor..cursor);
8936 }
8937 new_selection_ranges.push(selection.end..selection.end);
8938 to_unfold.push(selection.start..selection.end);
8939 }
8940 }
8941 self.unfold_ranges(&to_unfold, true, true, cx);
8942 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8943 s.select_ranges(new_selection_ranges);
8944 });
8945 }
8946
8947 pub fn add_selection_above(
8948 &mut self,
8949 _: &AddSelectionAbove,
8950 window: &mut Window,
8951 cx: &mut Context<Self>,
8952 ) {
8953 self.add_selection(true, window, cx);
8954 }
8955
8956 pub fn add_selection_below(
8957 &mut self,
8958 _: &AddSelectionBelow,
8959 window: &mut Window,
8960 cx: &mut Context<Self>,
8961 ) {
8962 self.add_selection(false, window, cx);
8963 }
8964
8965 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
8966 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8967 let mut selections = self.selections.all::<Point>(cx);
8968 let text_layout_details = self.text_layout_details(window);
8969 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8970 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8971 let range = oldest_selection.display_range(&display_map).sorted();
8972
8973 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8974 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8975 let positions = start_x.min(end_x)..start_x.max(end_x);
8976
8977 selections.clear();
8978 let mut stack = Vec::new();
8979 for row in range.start.row().0..=range.end.row().0 {
8980 if let Some(selection) = self.selections.build_columnar_selection(
8981 &display_map,
8982 DisplayRow(row),
8983 &positions,
8984 oldest_selection.reversed,
8985 &text_layout_details,
8986 ) {
8987 stack.push(selection.id);
8988 selections.push(selection);
8989 }
8990 }
8991
8992 if above {
8993 stack.reverse();
8994 }
8995
8996 AddSelectionsState { above, stack }
8997 });
8998
8999 let last_added_selection = *state.stack.last().unwrap();
9000 let mut new_selections = Vec::new();
9001 if above == state.above {
9002 let end_row = if above {
9003 DisplayRow(0)
9004 } else {
9005 display_map.max_point().row()
9006 };
9007
9008 'outer: for selection in selections {
9009 if selection.id == last_added_selection {
9010 let range = selection.display_range(&display_map).sorted();
9011 debug_assert_eq!(range.start.row(), range.end.row());
9012 let mut row = range.start.row();
9013 let positions =
9014 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
9015 px(start)..px(end)
9016 } else {
9017 let start_x =
9018 display_map.x_for_display_point(range.start, &text_layout_details);
9019 let end_x =
9020 display_map.x_for_display_point(range.end, &text_layout_details);
9021 start_x.min(end_x)..start_x.max(end_x)
9022 };
9023
9024 while row != end_row {
9025 if above {
9026 row.0 -= 1;
9027 } else {
9028 row.0 += 1;
9029 }
9030
9031 if let Some(new_selection) = self.selections.build_columnar_selection(
9032 &display_map,
9033 row,
9034 &positions,
9035 selection.reversed,
9036 &text_layout_details,
9037 ) {
9038 state.stack.push(new_selection.id);
9039 if above {
9040 new_selections.push(new_selection);
9041 new_selections.push(selection);
9042 } else {
9043 new_selections.push(selection);
9044 new_selections.push(new_selection);
9045 }
9046
9047 continue 'outer;
9048 }
9049 }
9050 }
9051
9052 new_selections.push(selection);
9053 }
9054 } else {
9055 new_selections = selections;
9056 new_selections.retain(|s| s.id != last_added_selection);
9057 state.stack.pop();
9058 }
9059
9060 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9061 s.select(new_selections);
9062 });
9063 if state.stack.len() > 1 {
9064 self.add_selections_state = Some(state);
9065 }
9066 }
9067
9068 pub fn select_next_match_internal(
9069 &mut self,
9070 display_map: &DisplaySnapshot,
9071 replace_newest: bool,
9072 autoscroll: Option<Autoscroll>,
9073 window: &mut Window,
9074 cx: &mut Context<Self>,
9075 ) -> Result<()> {
9076 fn select_next_match_ranges(
9077 this: &mut Editor,
9078 range: Range<usize>,
9079 replace_newest: bool,
9080 auto_scroll: Option<Autoscroll>,
9081 window: &mut Window,
9082 cx: &mut Context<Editor>,
9083 ) {
9084 this.unfold_ranges(&[range.clone()], false, true, cx);
9085 this.change_selections(auto_scroll, window, cx, |s| {
9086 if replace_newest {
9087 s.delete(s.newest_anchor().id);
9088 }
9089 s.insert_range(range.clone());
9090 });
9091 }
9092
9093 let buffer = &display_map.buffer_snapshot;
9094 let mut selections = self.selections.all::<usize>(cx);
9095 if let Some(mut select_next_state) = self.select_next_state.take() {
9096 let query = &select_next_state.query;
9097 if !select_next_state.done {
9098 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9099 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9100 let mut next_selected_range = None;
9101
9102 let bytes_after_last_selection =
9103 buffer.bytes_in_range(last_selection.end..buffer.len());
9104 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
9105 let query_matches = query
9106 .stream_find_iter(bytes_after_last_selection)
9107 .map(|result| (last_selection.end, result))
9108 .chain(
9109 query
9110 .stream_find_iter(bytes_before_first_selection)
9111 .map(|result| (0, result)),
9112 );
9113
9114 for (start_offset, query_match) in query_matches {
9115 let query_match = query_match.unwrap(); // can only fail due to I/O
9116 let offset_range =
9117 start_offset + query_match.start()..start_offset + query_match.end();
9118 let display_range = offset_range.start.to_display_point(display_map)
9119 ..offset_range.end.to_display_point(display_map);
9120
9121 if !select_next_state.wordwise
9122 || (!movement::is_inside_word(display_map, display_range.start)
9123 && !movement::is_inside_word(display_map, display_range.end))
9124 {
9125 // TODO: This is n^2, because we might check all the selections
9126 if !selections
9127 .iter()
9128 .any(|selection| selection.range().overlaps(&offset_range))
9129 {
9130 next_selected_range = Some(offset_range);
9131 break;
9132 }
9133 }
9134 }
9135
9136 if let Some(next_selected_range) = next_selected_range {
9137 select_next_match_ranges(
9138 self,
9139 next_selected_range,
9140 replace_newest,
9141 autoscroll,
9142 window,
9143 cx,
9144 );
9145 } else {
9146 select_next_state.done = true;
9147 }
9148 }
9149
9150 self.select_next_state = Some(select_next_state);
9151 } else {
9152 let mut only_carets = true;
9153 let mut same_text_selected = true;
9154 let mut selected_text = None;
9155
9156 let mut selections_iter = selections.iter().peekable();
9157 while let Some(selection) = selections_iter.next() {
9158 if selection.start != selection.end {
9159 only_carets = false;
9160 }
9161
9162 if same_text_selected {
9163 if selected_text.is_none() {
9164 selected_text =
9165 Some(buffer.text_for_range(selection.range()).collect::<String>());
9166 }
9167
9168 if let Some(next_selection) = selections_iter.peek() {
9169 if next_selection.range().len() == selection.range().len() {
9170 let next_selected_text = buffer
9171 .text_for_range(next_selection.range())
9172 .collect::<String>();
9173 if Some(next_selected_text) != selected_text {
9174 same_text_selected = false;
9175 selected_text = None;
9176 }
9177 } else {
9178 same_text_selected = false;
9179 selected_text = None;
9180 }
9181 }
9182 }
9183 }
9184
9185 if only_carets {
9186 for selection in &mut selections {
9187 let word_range = movement::surrounding_word(
9188 display_map,
9189 selection.start.to_display_point(display_map),
9190 );
9191 selection.start = word_range.start.to_offset(display_map, Bias::Left);
9192 selection.end = word_range.end.to_offset(display_map, Bias::Left);
9193 selection.goal = SelectionGoal::None;
9194 selection.reversed = false;
9195 select_next_match_ranges(
9196 self,
9197 selection.start..selection.end,
9198 replace_newest,
9199 autoscroll,
9200 window,
9201 cx,
9202 );
9203 }
9204
9205 if selections.len() == 1 {
9206 let selection = selections
9207 .last()
9208 .expect("ensured that there's only one selection");
9209 let query = buffer
9210 .text_for_range(selection.start..selection.end)
9211 .collect::<String>();
9212 let is_empty = query.is_empty();
9213 let select_state = SelectNextState {
9214 query: AhoCorasick::new(&[query])?,
9215 wordwise: true,
9216 done: is_empty,
9217 };
9218 self.select_next_state = Some(select_state);
9219 } else {
9220 self.select_next_state = None;
9221 }
9222 } else if let Some(selected_text) = selected_text {
9223 self.select_next_state = Some(SelectNextState {
9224 query: AhoCorasick::new(&[selected_text])?,
9225 wordwise: false,
9226 done: false,
9227 });
9228 self.select_next_match_internal(
9229 display_map,
9230 replace_newest,
9231 autoscroll,
9232 window,
9233 cx,
9234 )?;
9235 }
9236 }
9237 Ok(())
9238 }
9239
9240 pub fn select_all_matches(
9241 &mut self,
9242 _action: &SelectAllMatches,
9243 window: &mut Window,
9244 cx: &mut Context<Self>,
9245 ) -> Result<()> {
9246 self.push_to_selection_history();
9247 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9248
9249 self.select_next_match_internal(&display_map, false, None, window, cx)?;
9250 let Some(select_next_state) = self.select_next_state.as_mut() else {
9251 return Ok(());
9252 };
9253 if select_next_state.done {
9254 return Ok(());
9255 }
9256
9257 let mut new_selections = self.selections.all::<usize>(cx);
9258
9259 let buffer = &display_map.buffer_snapshot;
9260 let query_matches = select_next_state
9261 .query
9262 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
9263
9264 for query_match in query_matches {
9265 let query_match = query_match.unwrap(); // can only fail due to I/O
9266 let offset_range = query_match.start()..query_match.end();
9267 let display_range = offset_range.start.to_display_point(&display_map)
9268 ..offset_range.end.to_display_point(&display_map);
9269
9270 if !select_next_state.wordwise
9271 || (!movement::is_inside_word(&display_map, display_range.start)
9272 && !movement::is_inside_word(&display_map, display_range.end))
9273 {
9274 self.selections.change_with(cx, |selections| {
9275 new_selections.push(Selection {
9276 id: selections.new_selection_id(),
9277 start: offset_range.start,
9278 end: offset_range.end,
9279 reversed: false,
9280 goal: SelectionGoal::None,
9281 });
9282 });
9283 }
9284 }
9285
9286 new_selections.sort_by_key(|selection| selection.start);
9287 let mut ix = 0;
9288 while ix + 1 < new_selections.len() {
9289 let current_selection = &new_selections[ix];
9290 let next_selection = &new_selections[ix + 1];
9291 if current_selection.range().overlaps(&next_selection.range()) {
9292 if current_selection.id < next_selection.id {
9293 new_selections.remove(ix + 1);
9294 } else {
9295 new_selections.remove(ix);
9296 }
9297 } else {
9298 ix += 1;
9299 }
9300 }
9301
9302 let reversed = self.selections.oldest::<usize>(cx).reversed;
9303
9304 for selection in new_selections.iter_mut() {
9305 selection.reversed = reversed;
9306 }
9307
9308 select_next_state.done = true;
9309 self.unfold_ranges(
9310 &new_selections
9311 .iter()
9312 .map(|selection| selection.range())
9313 .collect::<Vec<_>>(),
9314 false,
9315 false,
9316 cx,
9317 );
9318 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
9319 selections.select(new_selections)
9320 });
9321
9322 Ok(())
9323 }
9324
9325 pub fn select_next(
9326 &mut self,
9327 action: &SelectNext,
9328 window: &mut Window,
9329 cx: &mut Context<Self>,
9330 ) -> Result<()> {
9331 self.push_to_selection_history();
9332 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9333 self.select_next_match_internal(
9334 &display_map,
9335 action.replace_newest,
9336 Some(Autoscroll::newest()),
9337 window,
9338 cx,
9339 )?;
9340 Ok(())
9341 }
9342
9343 pub fn select_previous(
9344 &mut self,
9345 action: &SelectPrevious,
9346 window: &mut Window,
9347 cx: &mut Context<Self>,
9348 ) -> Result<()> {
9349 self.push_to_selection_history();
9350 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9351 let buffer = &display_map.buffer_snapshot;
9352 let mut selections = self.selections.all::<usize>(cx);
9353 if let Some(mut select_prev_state) = self.select_prev_state.take() {
9354 let query = &select_prev_state.query;
9355 if !select_prev_state.done {
9356 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9357 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9358 let mut next_selected_range = None;
9359 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
9360 let bytes_before_last_selection =
9361 buffer.reversed_bytes_in_range(0..last_selection.start);
9362 let bytes_after_first_selection =
9363 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
9364 let query_matches = query
9365 .stream_find_iter(bytes_before_last_selection)
9366 .map(|result| (last_selection.start, result))
9367 .chain(
9368 query
9369 .stream_find_iter(bytes_after_first_selection)
9370 .map(|result| (buffer.len(), result)),
9371 );
9372 for (end_offset, query_match) in query_matches {
9373 let query_match = query_match.unwrap(); // can only fail due to I/O
9374 let offset_range =
9375 end_offset - query_match.end()..end_offset - query_match.start();
9376 let display_range = offset_range.start.to_display_point(&display_map)
9377 ..offset_range.end.to_display_point(&display_map);
9378
9379 if !select_prev_state.wordwise
9380 || (!movement::is_inside_word(&display_map, display_range.start)
9381 && !movement::is_inside_word(&display_map, display_range.end))
9382 {
9383 next_selected_range = Some(offset_range);
9384 break;
9385 }
9386 }
9387
9388 if let Some(next_selected_range) = next_selected_range {
9389 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
9390 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9391 if action.replace_newest {
9392 s.delete(s.newest_anchor().id);
9393 }
9394 s.insert_range(next_selected_range);
9395 });
9396 } else {
9397 select_prev_state.done = true;
9398 }
9399 }
9400
9401 self.select_prev_state = Some(select_prev_state);
9402 } else {
9403 let mut only_carets = true;
9404 let mut same_text_selected = true;
9405 let mut selected_text = None;
9406
9407 let mut selections_iter = selections.iter().peekable();
9408 while let Some(selection) = selections_iter.next() {
9409 if selection.start != selection.end {
9410 only_carets = false;
9411 }
9412
9413 if same_text_selected {
9414 if selected_text.is_none() {
9415 selected_text =
9416 Some(buffer.text_for_range(selection.range()).collect::<String>());
9417 }
9418
9419 if let Some(next_selection) = selections_iter.peek() {
9420 if next_selection.range().len() == selection.range().len() {
9421 let next_selected_text = buffer
9422 .text_for_range(next_selection.range())
9423 .collect::<String>();
9424 if Some(next_selected_text) != selected_text {
9425 same_text_selected = false;
9426 selected_text = None;
9427 }
9428 } else {
9429 same_text_selected = false;
9430 selected_text = None;
9431 }
9432 }
9433 }
9434 }
9435
9436 if only_carets {
9437 for selection in &mut selections {
9438 let word_range = movement::surrounding_word(
9439 &display_map,
9440 selection.start.to_display_point(&display_map),
9441 );
9442 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
9443 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
9444 selection.goal = SelectionGoal::None;
9445 selection.reversed = false;
9446 }
9447 if selections.len() == 1 {
9448 let selection = selections
9449 .last()
9450 .expect("ensured that there's only one selection");
9451 let query = buffer
9452 .text_for_range(selection.start..selection.end)
9453 .collect::<String>();
9454 let is_empty = query.is_empty();
9455 let select_state = SelectNextState {
9456 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
9457 wordwise: true,
9458 done: is_empty,
9459 };
9460 self.select_prev_state = Some(select_state);
9461 } else {
9462 self.select_prev_state = None;
9463 }
9464
9465 self.unfold_ranges(
9466 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9467 false,
9468 true,
9469 cx,
9470 );
9471 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9472 s.select(selections);
9473 });
9474 } else if let Some(selected_text) = selected_text {
9475 self.select_prev_state = Some(SelectNextState {
9476 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9477 wordwise: false,
9478 done: false,
9479 });
9480 self.select_previous(action, window, cx)?;
9481 }
9482 }
9483 Ok(())
9484 }
9485
9486 pub fn toggle_comments(
9487 &mut self,
9488 action: &ToggleComments,
9489 window: &mut Window,
9490 cx: &mut Context<Self>,
9491 ) {
9492 if self.read_only(cx) {
9493 return;
9494 }
9495 let text_layout_details = &self.text_layout_details(window);
9496 self.transact(window, cx, |this, window, cx| {
9497 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9498 let mut edits = Vec::new();
9499 let mut selection_edit_ranges = Vec::new();
9500 let mut last_toggled_row = None;
9501 let snapshot = this.buffer.read(cx).read(cx);
9502 let empty_str: Arc<str> = Arc::default();
9503 let mut suffixes_inserted = Vec::new();
9504 let ignore_indent = action.ignore_indent;
9505
9506 fn comment_prefix_range(
9507 snapshot: &MultiBufferSnapshot,
9508 row: MultiBufferRow,
9509 comment_prefix: &str,
9510 comment_prefix_whitespace: &str,
9511 ignore_indent: bool,
9512 ) -> Range<Point> {
9513 let indent_size = if ignore_indent {
9514 0
9515 } else {
9516 snapshot.indent_size_for_line(row).len
9517 };
9518
9519 let start = Point::new(row.0, indent_size);
9520
9521 let mut line_bytes = snapshot
9522 .bytes_in_range(start..snapshot.max_point())
9523 .flatten()
9524 .copied();
9525
9526 // If this line currently begins with the line comment prefix, then record
9527 // the range containing the prefix.
9528 if line_bytes
9529 .by_ref()
9530 .take(comment_prefix.len())
9531 .eq(comment_prefix.bytes())
9532 {
9533 // Include any whitespace that matches the comment prefix.
9534 let matching_whitespace_len = line_bytes
9535 .zip(comment_prefix_whitespace.bytes())
9536 .take_while(|(a, b)| a == b)
9537 .count() as u32;
9538 let end = Point::new(
9539 start.row,
9540 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9541 );
9542 start..end
9543 } else {
9544 start..start
9545 }
9546 }
9547
9548 fn comment_suffix_range(
9549 snapshot: &MultiBufferSnapshot,
9550 row: MultiBufferRow,
9551 comment_suffix: &str,
9552 comment_suffix_has_leading_space: bool,
9553 ) -> Range<Point> {
9554 let end = Point::new(row.0, snapshot.line_len(row));
9555 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9556
9557 let mut line_end_bytes = snapshot
9558 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9559 .flatten()
9560 .copied();
9561
9562 let leading_space_len = if suffix_start_column > 0
9563 && line_end_bytes.next() == Some(b' ')
9564 && comment_suffix_has_leading_space
9565 {
9566 1
9567 } else {
9568 0
9569 };
9570
9571 // If this line currently begins with the line comment prefix, then record
9572 // the range containing the prefix.
9573 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9574 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9575 start..end
9576 } else {
9577 end..end
9578 }
9579 }
9580
9581 // TODO: Handle selections that cross excerpts
9582 for selection in &mut selections {
9583 let start_column = snapshot
9584 .indent_size_for_line(MultiBufferRow(selection.start.row))
9585 .len;
9586 let language = if let Some(language) =
9587 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9588 {
9589 language
9590 } else {
9591 continue;
9592 };
9593
9594 selection_edit_ranges.clear();
9595
9596 // If multiple selections contain a given row, avoid processing that
9597 // row more than once.
9598 let mut start_row = MultiBufferRow(selection.start.row);
9599 if last_toggled_row == Some(start_row) {
9600 start_row = start_row.next_row();
9601 }
9602 let end_row =
9603 if selection.end.row > selection.start.row && selection.end.column == 0 {
9604 MultiBufferRow(selection.end.row - 1)
9605 } else {
9606 MultiBufferRow(selection.end.row)
9607 };
9608 last_toggled_row = Some(end_row);
9609
9610 if start_row > end_row {
9611 continue;
9612 }
9613
9614 // If the language has line comments, toggle those.
9615 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9616
9617 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9618 if ignore_indent {
9619 full_comment_prefixes = full_comment_prefixes
9620 .into_iter()
9621 .map(|s| Arc::from(s.trim_end()))
9622 .collect();
9623 }
9624
9625 if !full_comment_prefixes.is_empty() {
9626 let first_prefix = full_comment_prefixes
9627 .first()
9628 .expect("prefixes is non-empty");
9629 let prefix_trimmed_lengths = full_comment_prefixes
9630 .iter()
9631 .map(|p| p.trim_end_matches(' ').len())
9632 .collect::<SmallVec<[usize; 4]>>();
9633
9634 let mut all_selection_lines_are_comments = true;
9635
9636 for row in start_row.0..=end_row.0 {
9637 let row = MultiBufferRow(row);
9638 if start_row < end_row && snapshot.is_line_blank(row) {
9639 continue;
9640 }
9641
9642 let prefix_range = full_comment_prefixes
9643 .iter()
9644 .zip(prefix_trimmed_lengths.iter().copied())
9645 .map(|(prefix, trimmed_prefix_len)| {
9646 comment_prefix_range(
9647 snapshot.deref(),
9648 row,
9649 &prefix[..trimmed_prefix_len],
9650 &prefix[trimmed_prefix_len..],
9651 ignore_indent,
9652 )
9653 })
9654 .max_by_key(|range| range.end.column - range.start.column)
9655 .expect("prefixes is non-empty");
9656
9657 if prefix_range.is_empty() {
9658 all_selection_lines_are_comments = false;
9659 }
9660
9661 selection_edit_ranges.push(prefix_range);
9662 }
9663
9664 if all_selection_lines_are_comments {
9665 edits.extend(
9666 selection_edit_ranges
9667 .iter()
9668 .cloned()
9669 .map(|range| (range, empty_str.clone())),
9670 );
9671 } else {
9672 let min_column = selection_edit_ranges
9673 .iter()
9674 .map(|range| range.start.column)
9675 .min()
9676 .unwrap_or(0);
9677 edits.extend(selection_edit_ranges.iter().map(|range| {
9678 let position = Point::new(range.start.row, min_column);
9679 (position..position, first_prefix.clone())
9680 }));
9681 }
9682 } else if let Some((full_comment_prefix, comment_suffix)) =
9683 language.block_comment_delimiters()
9684 {
9685 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
9686 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
9687 let prefix_range = comment_prefix_range(
9688 snapshot.deref(),
9689 start_row,
9690 comment_prefix,
9691 comment_prefix_whitespace,
9692 ignore_indent,
9693 );
9694 let suffix_range = comment_suffix_range(
9695 snapshot.deref(),
9696 end_row,
9697 comment_suffix.trim_start_matches(' '),
9698 comment_suffix.starts_with(' '),
9699 );
9700
9701 if prefix_range.is_empty() || suffix_range.is_empty() {
9702 edits.push((
9703 prefix_range.start..prefix_range.start,
9704 full_comment_prefix.clone(),
9705 ));
9706 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9707 suffixes_inserted.push((end_row, comment_suffix.len()));
9708 } else {
9709 edits.push((prefix_range, empty_str.clone()));
9710 edits.push((suffix_range, empty_str.clone()));
9711 }
9712 } else {
9713 continue;
9714 }
9715 }
9716
9717 drop(snapshot);
9718 this.buffer.update(cx, |buffer, cx| {
9719 buffer.edit(edits, None, cx);
9720 });
9721
9722 // Adjust selections so that they end before any comment suffixes that
9723 // were inserted.
9724 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9725 let mut selections = this.selections.all::<Point>(cx);
9726 let snapshot = this.buffer.read(cx).read(cx);
9727 for selection in &mut selections {
9728 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
9729 match row.cmp(&MultiBufferRow(selection.end.row)) {
9730 Ordering::Less => {
9731 suffixes_inserted.next();
9732 continue;
9733 }
9734 Ordering::Greater => break,
9735 Ordering::Equal => {
9736 if selection.end.column == snapshot.line_len(row) {
9737 if selection.is_empty() {
9738 selection.start.column -= suffix_len as u32;
9739 }
9740 selection.end.column -= suffix_len as u32;
9741 }
9742 break;
9743 }
9744 }
9745 }
9746 }
9747
9748 drop(snapshot);
9749 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9750 s.select(selections)
9751 });
9752
9753 let selections = this.selections.all::<Point>(cx);
9754 let selections_on_single_row = selections.windows(2).all(|selections| {
9755 selections[0].start.row == selections[1].start.row
9756 && selections[0].end.row == selections[1].end.row
9757 && selections[0].start.row == selections[0].end.row
9758 });
9759 let selections_selecting = selections
9760 .iter()
9761 .any(|selection| selection.start != selection.end);
9762 let advance_downwards = action.advance_downwards
9763 && selections_on_single_row
9764 && !selections_selecting
9765 && !matches!(this.mode, EditorMode::SingleLine { .. });
9766
9767 if advance_downwards {
9768 let snapshot = this.buffer.read(cx).snapshot(cx);
9769
9770 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9771 s.move_cursors_with(|display_snapshot, display_point, _| {
9772 let mut point = display_point.to_point(display_snapshot);
9773 point.row += 1;
9774 point = snapshot.clip_point(point, Bias::Left);
9775 let display_point = point.to_display_point(display_snapshot);
9776 let goal = SelectionGoal::HorizontalPosition(
9777 display_snapshot
9778 .x_for_display_point(display_point, text_layout_details)
9779 .into(),
9780 );
9781 (display_point, goal)
9782 })
9783 });
9784 }
9785 });
9786 }
9787
9788 pub fn select_enclosing_symbol(
9789 &mut self,
9790 _: &SelectEnclosingSymbol,
9791 window: &mut Window,
9792 cx: &mut Context<Self>,
9793 ) {
9794 let buffer = self.buffer.read(cx).snapshot(cx);
9795 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9796
9797 fn update_selection(
9798 selection: &Selection<usize>,
9799 buffer_snap: &MultiBufferSnapshot,
9800 ) -> Option<Selection<usize>> {
9801 let cursor = selection.head();
9802 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
9803 for symbol in symbols.iter().rev() {
9804 let start = symbol.range.start.to_offset(buffer_snap);
9805 let end = symbol.range.end.to_offset(buffer_snap);
9806 let new_range = start..end;
9807 if start < selection.start || end > selection.end {
9808 return Some(Selection {
9809 id: selection.id,
9810 start: new_range.start,
9811 end: new_range.end,
9812 goal: SelectionGoal::None,
9813 reversed: selection.reversed,
9814 });
9815 }
9816 }
9817 None
9818 }
9819
9820 let mut selected_larger_symbol = false;
9821 let new_selections = old_selections
9822 .iter()
9823 .map(|selection| match update_selection(selection, &buffer) {
9824 Some(new_selection) => {
9825 if new_selection.range() != selection.range() {
9826 selected_larger_symbol = true;
9827 }
9828 new_selection
9829 }
9830 None => selection.clone(),
9831 })
9832 .collect::<Vec<_>>();
9833
9834 if selected_larger_symbol {
9835 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9836 s.select(new_selections);
9837 });
9838 }
9839 }
9840
9841 pub fn select_larger_syntax_node(
9842 &mut self,
9843 _: &SelectLargerSyntaxNode,
9844 window: &mut Window,
9845 cx: &mut Context<Self>,
9846 ) {
9847 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9848 let buffer = self.buffer.read(cx).snapshot(cx);
9849 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9850
9851 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9852 let mut selected_larger_node = false;
9853 let new_selections = old_selections
9854 .iter()
9855 .map(|selection| {
9856 let old_range = selection.start..selection.end;
9857 let mut new_range = old_range.clone();
9858 let mut new_node = None;
9859 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
9860 {
9861 new_node = Some(node);
9862 new_range = containing_range;
9863 if !display_map.intersects_fold(new_range.start)
9864 && !display_map.intersects_fold(new_range.end)
9865 {
9866 break;
9867 }
9868 }
9869
9870 if let Some(node) = new_node {
9871 // Log the ancestor, to support using this action as a way to explore TreeSitter
9872 // nodes. Parent and grandparent are also logged because this operation will not
9873 // visit nodes that have the same range as their parent.
9874 log::info!("Node: {node:?}");
9875 let parent = node.parent();
9876 log::info!("Parent: {parent:?}");
9877 let grandparent = parent.and_then(|x| x.parent());
9878 log::info!("Grandparent: {grandparent:?}");
9879 }
9880
9881 selected_larger_node |= new_range != old_range;
9882 Selection {
9883 id: selection.id,
9884 start: new_range.start,
9885 end: new_range.end,
9886 goal: SelectionGoal::None,
9887 reversed: selection.reversed,
9888 }
9889 })
9890 .collect::<Vec<_>>();
9891
9892 if selected_larger_node {
9893 stack.push(old_selections);
9894 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9895 s.select(new_selections);
9896 });
9897 }
9898 self.select_larger_syntax_node_stack = stack;
9899 }
9900
9901 pub fn select_smaller_syntax_node(
9902 &mut self,
9903 _: &SelectSmallerSyntaxNode,
9904 window: &mut Window,
9905 cx: &mut Context<Self>,
9906 ) {
9907 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9908 if let Some(selections) = stack.pop() {
9909 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9910 s.select(selections.to_vec());
9911 });
9912 }
9913 self.select_larger_syntax_node_stack = stack;
9914 }
9915
9916 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
9917 if !EditorSettings::get_global(cx).gutter.runnables {
9918 self.clear_tasks();
9919 return Task::ready(());
9920 }
9921 let project = self.project.as_ref().map(Entity::downgrade);
9922 cx.spawn_in(window, |this, mut cx| async move {
9923 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
9924 let Some(project) = project.and_then(|p| p.upgrade()) else {
9925 return;
9926 };
9927 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9928 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9929 }) else {
9930 return;
9931 };
9932
9933 let hide_runnables = project
9934 .update(&mut cx, |project, cx| {
9935 // Do not display any test indicators in non-dev server remote projects.
9936 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9937 })
9938 .unwrap_or(true);
9939 if hide_runnables {
9940 return;
9941 }
9942 let new_rows =
9943 cx.background_executor()
9944 .spawn({
9945 let snapshot = display_snapshot.clone();
9946 async move {
9947 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9948 }
9949 })
9950 .await;
9951
9952 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9953 this.update(&mut cx, |this, _| {
9954 this.clear_tasks();
9955 for (key, value) in rows {
9956 this.insert_tasks(key, value);
9957 }
9958 })
9959 .ok();
9960 })
9961 }
9962 fn fetch_runnable_ranges(
9963 snapshot: &DisplaySnapshot,
9964 range: Range<Anchor>,
9965 ) -> Vec<language::RunnableRange> {
9966 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9967 }
9968
9969 fn runnable_rows(
9970 project: Entity<Project>,
9971 snapshot: DisplaySnapshot,
9972 runnable_ranges: Vec<RunnableRange>,
9973 mut cx: AsyncWindowContext,
9974 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9975 runnable_ranges
9976 .into_iter()
9977 .filter_map(|mut runnable| {
9978 let tasks = cx
9979 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9980 .ok()?;
9981 if tasks.is_empty() {
9982 return None;
9983 }
9984
9985 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9986
9987 let row = snapshot
9988 .buffer_snapshot
9989 .buffer_line_for_row(MultiBufferRow(point.row))?
9990 .1
9991 .start
9992 .row;
9993
9994 let context_range =
9995 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9996 Some((
9997 (runnable.buffer_id, row),
9998 RunnableTasks {
9999 templates: tasks,
10000 offset: MultiBufferOffset(runnable.run_range.start),
10001 context_range,
10002 column: point.column,
10003 extra_variables: runnable.extra_captures,
10004 },
10005 ))
10006 })
10007 .collect()
10008 }
10009
10010 fn templates_with_tags(
10011 project: &Entity<Project>,
10012 runnable: &mut Runnable,
10013 cx: &mut App,
10014 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
10015 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
10016 let (worktree_id, file) = project
10017 .buffer_for_id(runnable.buffer, cx)
10018 .and_then(|buffer| buffer.read(cx).file())
10019 .map(|file| (file.worktree_id(cx), file.clone()))
10020 .unzip();
10021
10022 (
10023 project.task_store().read(cx).task_inventory().cloned(),
10024 worktree_id,
10025 file,
10026 )
10027 });
10028
10029 let tags = mem::take(&mut runnable.tags);
10030 let mut tags: Vec<_> = tags
10031 .into_iter()
10032 .flat_map(|tag| {
10033 let tag = tag.0.clone();
10034 inventory
10035 .as_ref()
10036 .into_iter()
10037 .flat_map(|inventory| {
10038 inventory.read(cx).list_tasks(
10039 file.clone(),
10040 Some(runnable.language.clone()),
10041 worktree_id,
10042 cx,
10043 )
10044 })
10045 .filter(move |(_, template)| {
10046 template.tags.iter().any(|source_tag| source_tag == &tag)
10047 })
10048 })
10049 .sorted_by_key(|(kind, _)| kind.to_owned())
10050 .collect();
10051 if let Some((leading_tag_source, _)) = tags.first() {
10052 // Strongest source wins; if we have worktree tag binding, prefer that to
10053 // global and language bindings;
10054 // if we have a global binding, prefer that to language binding.
10055 let first_mismatch = tags
10056 .iter()
10057 .position(|(tag_source, _)| tag_source != leading_tag_source);
10058 if let Some(index) = first_mismatch {
10059 tags.truncate(index);
10060 }
10061 }
10062
10063 tags
10064 }
10065
10066 pub fn move_to_enclosing_bracket(
10067 &mut self,
10068 _: &MoveToEnclosingBracket,
10069 window: &mut Window,
10070 cx: &mut Context<Self>,
10071 ) {
10072 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10073 s.move_offsets_with(|snapshot, selection| {
10074 let Some(enclosing_bracket_ranges) =
10075 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
10076 else {
10077 return;
10078 };
10079
10080 let mut best_length = usize::MAX;
10081 let mut best_inside = false;
10082 let mut best_in_bracket_range = false;
10083 let mut best_destination = None;
10084 for (open, close) in enclosing_bracket_ranges {
10085 let close = close.to_inclusive();
10086 let length = close.end() - open.start;
10087 let inside = selection.start >= open.end && selection.end <= *close.start();
10088 let in_bracket_range = open.to_inclusive().contains(&selection.head())
10089 || close.contains(&selection.head());
10090
10091 // If best is next to a bracket and current isn't, skip
10092 if !in_bracket_range && best_in_bracket_range {
10093 continue;
10094 }
10095
10096 // Prefer smaller lengths unless best is inside and current isn't
10097 if length > best_length && (best_inside || !inside) {
10098 continue;
10099 }
10100
10101 best_length = length;
10102 best_inside = inside;
10103 best_in_bracket_range = in_bracket_range;
10104 best_destination = Some(
10105 if close.contains(&selection.start) && close.contains(&selection.end) {
10106 if inside {
10107 open.end
10108 } else {
10109 open.start
10110 }
10111 } else if inside {
10112 *close.start()
10113 } else {
10114 *close.end()
10115 },
10116 );
10117 }
10118
10119 if let Some(destination) = best_destination {
10120 selection.collapse_to(destination, SelectionGoal::None);
10121 }
10122 })
10123 });
10124 }
10125
10126 pub fn undo_selection(
10127 &mut self,
10128 _: &UndoSelection,
10129 window: &mut Window,
10130 cx: &mut Context<Self>,
10131 ) {
10132 self.end_selection(window, cx);
10133 self.selection_history.mode = SelectionHistoryMode::Undoing;
10134 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
10135 self.change_selections(None, window, cx, |s| {
10136 s.select_anchors(entry.selections.to_vec())
10137 });
10138 self.select_next_state = entry.select_next_state;
10139 self.select_prev_state = entry.select_prev_state;
10140 self.add_selections_state = entry.add_selections_state;
10141 self.request_autoscroll(Autoscroll::newest(), cx);
10142 }
10143 self.selection_history.mode = SelectionHistoryMode::Normal;
10144 }
10145
10146 pub fn redo_selection(
10147 &mut self,
10148 _: &RedoSelection,
10149 window: &mut Window,
10150 cx: &mut Context<Self>,
10151 ) {
10152 self.end_selection(window, cx);
10153 self.selection_history.mode = SelectionHistoryMode::Redoing;
10154 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
10155 self.change_selections(None, window, cx, |s| {
10156 s.select_anchors(entry.selections.to_vec())
10157 });
10158 self.select_next_state = entry.select_next_state;
10159 self.select_prev_state = entry.select_prev_state;
10160 self.add_selections_state = entry.add_selections_state;
10161 self.request_autoscroll(Autoscroll::newest(), cx);
10162 }
10163 self.selection_history.mode = SelectionHistoryMode::Normal;
10164 }
10165
10166 pub fn expand_excerpts(
10167 &mut self,
10168 action: &ExpandExcerpts,
10169 _: &mut Window,
10170 cx: &mut Context<Self>,
10171 ) {
10172 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
10173 }
10174
10175 pub fn expand_excerpts_down(
10176 &mut self,
10177 action: &ExpandExcerptsDown,
10178 _: &mut Window,
10179 cx: &mut Context<Self>,
10180 ) {
10181 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
10182 }
10183
10184 pub fn expand_excerpts_up(
10185 &mut self,
10186 action: &ExpandExcerptsUp,
10187 _: &mut Window,
10188 cx: &mut Context<Self>,
10189 ) {
10190 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
10191 }
10192
10193 pub fn expand_excerpts_for_direction(
10194 &mut self,
10195 lines: u32,
10196 direction: ExpandExcerptDirection,
10197
10198 cx: &mut Context<Self>,
10199 ) {
10200 let selections = self.selections.disjoint_anchors();
10201
10202 let lines = if lines == 0 {
10203 EditorSettings::get_global(cx).expand_excerpt_lines
10204 } else {
10205 lines
10206 };
10207
10208 self.buffer.update(cx, |buffer, cx| {
10209 let snapshot = buffer.snapshot(cx);
10210 let mut excerpt_ids = selections
10211 .iter()
10212 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
10213 .collect::<Vec<_>>();
10214 excerpt_ids.sort();
10215 excerpt_ids.dedup();
10216 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
10217 })
10218 }
10219
10220 pub fn expand_excerpt(
10221 &mut self,
10222 excerpt: ExcerptId,
10223 direction: ExpandExcerptDirection,
10224 cx: &mut Context<Self>,
10225 ) {
10226 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
10227 self.buffer.update(cx, |buffer, cx| {
10228 buffer.expand_excerpts([excerpt], lines, direction, cx)
10229 })
10230 }
10231
10232 pub fn go_to_singleton_buffer_point(
10233 &mut self,
10234 point: Point,
10235 window: &mut Window,
10236 cx: &mut Context<Self>,
10237 ) {
10238 self.go_to_singleton_buffer_range(point..point, window, cx);
10239 }
10240
10241 pub fn go_to_singleton_buffer_range(
10242 &mut self,
10243 range: Range<Point>,
10244 window: &mut Window,
10245 cx: &mut Context<Self>,
10246 ) {
10247 let multibuffer = self.buffer().read(cx);
10248 let Some(buffer) = multibuffer.as_singleton() else {
10249 return;
10250 };
10251 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
10252 return;
10253 };
10254 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
10255 return;
10256 };
10257 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
10258 s.select_anchor_ranges([start..end])
10259 });
10260 }
10261
10262 fn go_to_diagnostic(
10263 &mut self,
10264 _: &GoToDiagnostic,
10265 window: &mut Window,
10266 cx: &mut Context<Self>,
10267 ) {
10268 self.go_to_diagnostic_impl(Direction::Next, window, cx)
10269 }
10270
10271 fn go_to_prev_diagnostic(
10272 &mut self,
10273 _: &GoToPrevDiagnostic,
10274 window: &mut Window,
10275 cx: &mut Context<Self>,
10276 ) {
10277 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
10278 }
10279
10280 pub fn go_to_diagnostic_impl(
10281 &mut self,
10282 direction: Direction,
10283 window: &mut Window,
10284 cx: &mut Context<Self>,
10285 ) {
10286 let buffer = self.buffer.read(cx).snapshot(cx);
10287 let selection = self.selections.newest::<usize>(cx);
10288
10289 // If there is an active Diagnostic Popover jump to its diagnostic instead.
10290 if direction == Direction::Next {
10291 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
10292 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
10293 return;
10294 };
10295 self.activate_diagnostics(
10296 buffer_id,
10297 popover.local_diagnostic.diagnostic.group_id,
10298 window,
10299 cx,
10300 );
10301 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
10302 let primary_range_start = active_diagnostics.primary_range.start;
10303 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10304 let mut new_selection = s.newest_anchor().clone();
10305 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
10306 s.select_anchors(vec![new_selection.clone()]);
10307 });
10308 self.refresh_inline_completion(false, true, window, cx);
10309 }
10310 return;
10311 }
10312 }
10313
10314 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
10315 active_diagnostics
10316 .primary_range
10317 .to_offset(&buffer)
10318 .to_inclusive()
10319 });
10320 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
10321 if active_primary_range.contains(&selection.head()) {
10322 *active_primary_range.start()
10323 } else {
10324 selection.head()
10325 }
10326 } else {
10327 selection.head()
10328 };
10329 let snapshot = self.snapshot(window, cx);
10330 loop {
10331 let mut diagnostics;
10332 if direction == Direction::Prev {
10333 diagnostics = buffer
10334 .diagnostics_in_range::<usize>(0..search_start)
10335 .collect::<Vec<_>>();
10336 diagnostics.reverse();
10337 } else {
10338 diagnostics = buffer
10339 .diagnostics_in_range::<usize>(search_start..buffer.len())
10340 .collect::<Vec<_>>();
10341 };
10342 let group = diagnostics
10343 .into_iter()
10344 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
10345 // relies on diagnostics_in_range to return diagnostics with the same starting range to
10346 // be sorted in a stable way
10347 // skip until we are at current active diagnostic, if it exists
10348 .skip_while(|entry| {
10349 let is_in_range = match direction {
10350 Direction::Prev => entry.range.end > search_start,
10351 Direction::Next => entry.range.start < search_start,
10352 };
10353 is_in_range
10354 && self
10355 .active_diagnostics
10356 .as_ref()
10357 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
10358 })
10359 .find_map(|entry| {
10360 if entry.diagnostic.is_primary
10361 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
10362 && entry.range.start != entry.range.end
10363 // if we match with the active diagnostic, skip it
10364 && Some(entry.diagnostic.group_id)
10365 != self.active_diagnostics.as_ref().map(|d| d.group_id)
10366 {
10367 Some((entry.range, entry.diagnostic.group_id))
10368 } else {
10369 None
10370 }
10371 });
10372
10373 if let Some((primary_range, group_id)) = group {
10374 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
10375 return;
10376 };
10377 self.activate_diagnostics(buffer_id, group_id, window, cx);
10378 if self.active_diagnostics.is_some() {
10379 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10380 s.select(vec![Selection {
10381 id: selection.id,
10382 start: primary_range.start,
10383 end: primary_range.start,
10384 reversed: false,
10385 goal: SelectionGoal::None,
10386 }]);
10387 });
10388 self.refresh_inline_completion(false, true, window, cx);
10389 }
10390 break;
10391 } else {
10392 // Cycle around to the start of the buffer, potentially moving back to the start of
10393 // the currently active diagnostic.
10394 active_primary_range.take();
10395 if direction == Direction::Prev {
10396 if search_start == buffer.len() {
10397 break;
10398 } else {
10399 search_start = buffer.len();
10400 }
10401 } else if search_start == 0 {
10402 break;
10403 } else {
10404 search_start = 0;
10405 }
10406 }
10407 }
10408 }
10409
10410 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
10411 let snapshot = self.snapshot(window, cx);
10412 let selection = self.selections.newest::<Point>(cx);
10413 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
10414 }
10415
10416 fn go_to_hunk_after_position(
10417 &mut self,
10418 snapshot: &EditorSnapshot,
10419 position: Point,
10420 window: &mut Window,
10421 cx: &mut Context<Editor>,
10422 ) -> Option<MultiBufferDiffHunk> {
10423 let mut hunk = snapshot
10424 .buffer_snapshot
10425 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
10426 .find(|hunk| hunk.row_range.start.0 > position.row);
10427 if hunk.is_none() {
10428 hunk = snapshot
10429 .buffer_snapshot
10430 .diff_hunks_in_range(Point::zero()..position)
10431 .find(|hunk| hunk.row_range.end.0 < position.row)
10432 }
10433 if let Some(hunk) = &hunk {
10434 let destination = Point::new(hunk.row_range.start.0, 0);
10435 self.unfold_ranges(&[destination..destination], false, false, cx);
10436 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10437 s.select_ranges(vec![destination..destination]);
10438 });
10439 }
10440
10441 hunk
10442 }
10443
10444 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
10445 let snapshot = self.snapshot(window, cx);
10446 let selection = self.selections.newest::<Point>(cx);
10447 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
10448 }
10449
10450 fn go_to_hunk_before_position(
10451 &mut self,
10452 snapshot: &EditorSnapshot,
10453 position: Point,
10454 window: &mut Window,
10455 cx: &mut Context<Editor>,
10456 ) -> Option<MultiBufferDiffHunk> {
10457 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
10458 if hunk.is_none() {
10459 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
10460 }
10461 if let Some(hunk) = &hunk {
10462 let destination = Point::new(hunk.row_range.start.0, 0);
10463 self.unfold_ranges(&[destination..destination], false, false, cx);
10464 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10465 s.select_ranges(vec![destination..destination]);
10466 });
10467 }
10468
10469 hunk
10470 }
10471
10472 pub fn go_to_definition(
10473 &mut self,
10474 _: &GoToDefinition,
10475 window: &mut Window,
10476 cx: &mut Context<Self>,
10477 ) -> Task<Result<Navigated>> {
10478 let definition =
10479 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10480 cx.spawn_in(window, |editor, mut cx| async move {
10481 if definition.await? == Navigated::Yes {
10482 return Ok(Navigated::Yes);
10483 }
10484 match editor.update_in(&mut cx, |editor, window, cx| {
10485 editor.find_all_references(&FindAllReferences, window, cx)
10486 })? {
10487 Some(references) => references.await,
10488 None => Ok(Navigated::No),
10489 }
10490 })
10491 }
10492
10493 pub fn go_to_declaration(
10494 &mut self,
10495 _: &GoToDeclaration,
10496 window: &mut Window,
10497 cx: &mut Context<Self>,
10498 ) -> Task<Result<Navigated>> {
10499 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
10500 }
10501
10502 pub fn go_to_declaration_split(
10503 &mut self,
10504 _: &GoToDeclaration,
10505 window: &mut Window,
10506 cx: &mut Context<Self>,
10507 ) -> Task<Result<Navigated>> {
10508 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10509 }
10510
10511 pub fn go_to_implementation(
10512 &mut self,
10513 _: &GoToImplementation,
10514 window: &mut Window,
10515 cx: &mut Context<Self>,
10516 ) -> Task<Result<Navigated>> {
10517 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10518 }
10519
10520 pub fn go_to_implementation_split(
10521 &mut self,
10522 _: &GoToImplementationSplit,
10523 window: &mut Window,
10524 cx: &mut Context<Self>,
10525 ) -> Task<Result<Navigated>> {
10526 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10527 }
10528
10529 pub fn go_to_type_definition(
10530 &mut self,
10531 _: &GoToTypeDefinition,
10532 window: &mut Window,
10533 cx: &mut Context<Self>,
10534 ) -> Task<Result<Navigated>> {
10535 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10536 }
10537
10538 pub fn go_to_definition_split(
10539 &mut self,
10540 _: &GoToDefinitionSplit,
10541 window: &mut Window,
10542 cx: &mut Context<Self>,
10543 ) -> Task<Result<Navigated>> {
10544 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10545 }
10546
10547 pub fn go_to_type_definition_split(
10548 &mut self,
10549 _: &GoToTypeDefinitionSplit,
10550 window: &mut Window,
10551 cx: &mut Context<Self>,
10552 ) -> Task<Result<Navigated>> {
10553 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10554 }
10555
10556 fn go_to_definition_of_kind(
10557 &mut self,
10558 kind: GotoDefinitionKind,
10559 split: bool,
10560 window: &mut Window,
10561 cx: &mut Context<Self>,
10562 ) -> Task<Result<Navigated>> {
10563 let Some(provider) = self.semantics_provider.clone() else {
10564 return Task::ready(Ok(Navigated::No));
10565 };
10566 let head = self.selections.newest::<usize>(cx).head();
10567 let buffer = self.buffer.read(cx);
10568 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10569 text_anchor
10570 } else {
10571 return Task::ready(Ok(Navigated::No));
10572 };
10573
10574 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10575 return Task::ready(Ok(Navigated::No));
10576 };
10577
10578 cx.spawn_in(window, |editor, mut cx| async move {
10579 let definitions = definitions.await?;
10580 let navigated = editor
10581 .update_in(&mut cx, |editor, window, cx| {
10582 editor.navigate_to_hover_links(
10583 Some(kind),
10584 definitions
10585 .into_iter()
10586 .filter(|location| {
10587 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10588 })
10589 .map(HoverLink::Text)
10590 .collect::<Vec<_>>(),
10591 split,
10592 window,
10593 cx,
10594 )
10595 })?
10596 .await?;
10597 anyhow::Ok(navigated)
10598 })
10599 }
10600
10601 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10602 let selection = self.selections.newest_anchor();
10603 let head = selection.head();
10604 let tail = selection.tail();
10605
10606 let Some((buffer, start_position)) =
10607 self.buffer.read(cx).text_anchor_for_position(head, cx)
10608 else {
10609 return;
10610 };
10611
10612 let end_position = if head != tail {
10613 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10614 return;
10615 };
10616 Some(pos)
10617 } else {
10618 None
10619 };
10620
10621 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10622 let url = if let Some(end_pos) = end_position {
10623 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10624 } else {
10625 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10626 };
10627
10628 if let Some(url) = url {
10629 editor.update(&mut cx, |_, cx| {
10630 cx.open_url(&url);
10631 })
10632 } else {
10633 Ok(())
10634 }
10635 });
10636
10637 url_finder.detach();
10638 }
10639
10640 pub fn open_selected_filename(
10641 &mut self,
10642 _: &OpenSelectedFilename,
10643 window: &mut Window,
10644 cx: &mut Context<Self>,
10645 ) {
10646 let Some(workspace) = self.workspace() else {
10647 return;
10648 };
10649
10650 let position = self.selections.newest_anchor().head();
10651
10652 let Some((buffer, buffer_position)) =
10653 self.buffer.read(cx).text_anchor_for_position(position, cx)
10654 else {
10655 return;
10656 };
10657
10658 let project = self.project.clone();
10659
10660 cx.spawn_in(window, |_, mut cx| async move {
10661 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10662
10663 if let Some((_, path)) = result {
10664 workspace
10665 .update_in(&mut cx, |workspace, window, cx| {
10666 workspace.open_resolved_path(path, window, cx)
10667 })?
10668 .await?;
10669 }
10670 anyhow::Ok(())
10671 })
10672 .detach();
10673 }
10674
10675 pub(crate) fn navigate_to_hover_links(
10676 &mut self,
10677 kind: Option<GotoDefinitionKind>,
10678 mut definitions: Vec<HoverLink>,
10679 split: bool,
10680 window: &mut Window,
10681 cx: &mut Context<Editor>,
10682 ) -> Task<Result<Navigated>> {
10683 // If there is one definition, just open it directly
10684 if definitions.len() == 1 {
10685 let definition = definitions.pop().unwrap();
10686
10687 enum TargetTaskResult {
10688 Location(Option<Location>),
10689 AlreadyNavigated,
10690 }
10691
10692 let target_task = match definition {
10693 HoverLink::Text(link) => {
10694 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
10695 }
10696 HoverLink::InlayHint(lsp_location, server_id) => {
10697 let computation =
10698 self.compute_target_location(lsp_location, server_id, window, cx);
10699 cx.background_executor().spawn(async move {
10700 let location = computation.await?;
10701 Ok(TargetTaskResult::Location(location))
10702 })
10703 }
10704 HoverLink::Url(url) => {
10705 cx.open_url(&url);
10706 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
10707 }
10708 HoverLink::File(path) => {
10709 if let Some(workspace) = self.workspace() {
10710 cx.spawn_in(window, |_, mut cx| async move {
10711 workspace
10712 .update_in(&mut cx, |workspace, window, cx| {
10713 workspace.open_resolved_path(path, window, cx)
10714 })?
10715 .await
10716 .map(|_| TargetTaskResult::AlreadyNavigated)
10717 })
10718 } else {
10719 Task::ready(Ok(TargetTaskResult::Location(None)))
10720 }
10721 }
10722 };
10723 cx.spawn_in(window, |editor, mut cx| async move {
10724 let target = match target_task.await.context("target resolution task")? {
10725 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
10726 TargetTaskResult::Location(None) => return Ok(Navigated::No),
10727 TargetTaskResult::Location(Some(target)) => target,
10728 };
10729
10730 editor.update_in(&mut cx, |editor, window, cx| {
10731 let Some(workspace) = editor.workspace() else {
10732 return Navigated::No;
10733 };
10734 let pane = workspace.read(cx).active_pane().clone();
10735
10736 let range = target.range.to_point(target.buffer.read(cx));
10737 let range = editor.range_for_match(&range);
10738 let range = collapse_multiline_range(range);
10739
10740 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
10741 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
10742 } else {
10743 window.defer(cx, move |window, cx| {
10744 let target_editor: Entity<Self> =
10745 workspace.update(cx, |workspace, cx| {
10746 let pane = if split {
10747 workspace.adjacent_pane(window, cx)
10748 } else {
10749 workspace.active_pane().clone()
10750 };
10751
10752 workspace.open_project_item(
10753 pane,
10754 target.buffer.clone(),
10755 true,
10756 true,
10757 window,
10758 cx,
10759 )
10760 });
10761 target_editor.update(cx, |target_editor, cx| {
10762 // When selecting a definition in a different buffer, disable the nav history
10763 // to avoid creating a history entry at the previous cursor location.
10764 pane.update(cx, |pane, _| pane.disable_history());
10765 target_editor.go_to_singleton_buffer_range(range, window, cx);
10766 pane.update(cx, |pane, _| pane.enable_history());
10767 });
10768 });
10769 }
10770 Navigated::Yes
10771 })
10772 })
10773 } else if !definitions.is_empty() {
10774 cx.spawn_in(window, |editor, mut cx| async move {
10775 let (title, location_tasks, workspace) = editor
10776 .update_in(&mut cx, |editor, window, cx| {
10777 let tab_kind = match kind {
10778 Some(GotoDefinitionKind::Implementation) => "Implementations",
10779 _ => "Definitions",
10780 };
10781 let title = definitions
10782 .iter()
10783 .find_map(|definition| match definition {
10784 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
10785 let buffer = origin.buffer.read(cx);
10786 format!(
10787 "{} for {}",
10788 tab_kind,
10789 buffer
10790 .text_for_range(origin.range.clone())
10791 .collect::<String>()
10792 )
10793 }),
10794 HoverLink::InlayHint(_, _) => None,
10795 HoverLink::Url(_) => None,
10796 HoverLink::File(_) => None,
10797 })
10798 .unwrap_or(tab_kind.to_string());
10799 let location_tasks = definitions
10800 .into_iter()
10801 .map(|definition| match definition {
10802 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
10803 HoverLink::InlayHint(lsp_location, server_id) => editor
10804 .compute_target_location(lsp_location, server_id, window, cx),
10805 HoverLink::Url(_) => Task::ready(Ok(None)),
10806 HoverLink::File(_) => Task::ready(Ok(None)),
10807 })
10808 .collect::<Vec<_>>();
10809 (title, location_tasks, editor.workspace().clone())
10810 })
10811 .context("location tasks preparation")?;
10812
10813 let locations = future::join_all(location_tasks)
10814 .await
10815 .into_iter()
10816 .filter_map(|location| location.transpose())
10817 .collect::<Result<_>>()
10818 .context("location tasks")?;
10819
10820 let Some(workspace) = workspace else {
10821 return Ok(Navigated::No);
10822 };
10823 let opened = workspace
10824 .update_in(&mut cx, |workspace, window, cx| {
10825 Self::open_locations_in_multibuffer(
10826 workspace,
10827 locations,
10828 title,
10829 split,
10830 MultibufferSelectionMode::First,
10831 window,
10832 cx,
10833 )
10834 })
10835 .ok();
10836
10837 anyhow::Ok(Navigated::from_bool(opened.is_some()))
10838 })
10839 } else {
10840 Task::ready(Ok(Navigated::No))
10841 }
10842 }
10843
10844 fn compute_target_location(
10845 &self,
10846 lsp_location: lsp::Location,
10847 server_id: LanguageServerId,
10848 window: &mut Window,
10849 cx: &mut Context<Self>,
10850 ) -> Task<anyhow::Result<Option<Location>>> {
10851 let Some(project) = self.project.clone() else {
10852 return Task::ready(Ok(None));
10853 };
10854
10855 cx.spawn_in(window, move |editor, mut cx| async move {
10856 let location_task = editor.update(&mut cx, |_, cx| {
10857 project.update(cx, |project, cx| {
10858 let language_server_name = project
10859 .language_server_statuses(cx)
10860 .find(|(id, _)| server_id == *id)
10861 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
10862 language_server_name.map(|language_server_name| {
10863 project.open_local_buffer_via_lsp(
10864 lsp_location.uri.clone(),
10865 server_id,
10866 language_server_name,
10867 cx,
10868 )
10869 })
10870 })
10871 })?;
10872 let location = match location_task {
10873 Some(task) => Some({
10874 let target_buffer_handle = task.await.context("open local buffer")?;
10875 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
10876 let target_start = target_buffer
10877 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
10878 let target_end = target_buffer
10879 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
10880 target_buffer.anchor_after(target_start)
10881 ..target_buffer.anchor_before(target_end)
10882 })?;
10883 Location {
10884 buffer: target_buffer_handle,
10885 range,
10886 }
10887 }),
10888 None => None,
10889 };
10890 Ok(location)
10891 })
10892 }
10893
10894 pub fn find_all_references(
10895 &mut self,
10896 _: &FindAllReferences,
10897 window: &mut Window,
10898 cx: &mut Context<Self>,
10899 ) -> Option<Task<Result<Navigated>>> {
10900 let selection = self.selections.newest::<usize>(cx);
10901 let multi_buffer = self.buffer.read(cx);
10902 let head = selection.head();
10903
10904 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10905 let head_anchor = multi_buffer_snapshot.anchor_at(
10906 head,
10907 if head < selection.tail() {
10908 Bias::Right
10909 } else {
10910 Bias::Left
10911 },
10912 );
10913
10914 match self
10915 .find_all_references_task_sources
10916 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
10917 {
10918 Ok(_) => {
10919 log::info!(
10920 "Ignoring repeated FindAllReferences invocation with the position of already running task"
10921 );
10922 return None;
10923 }
10924 Err(i) => {
10925 self.find_all_references_task_sources.insert(i, head_anchor);
10926 }
10927 }
10928
10929 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
10930 let workspace = self.workspace()?;
10931 let project = workspace.read(cx).project().clone();
10932 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
10933 Some(cx.spawn_in(window, |editor, mut cx| async move {
10934 let _cleanup = defer({
10935 let mut cx = cx.clone();
10936 move || {
10937 let _ = editor.update(&mut cx, |editor, _| {
10938 if let Ok(i) =
10939 editor
10940 .find_all_references_task_sources
10941 .binary_search_by(|anchor| {
10942 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
10943 })
10944 {
10945 editor.find_all_references_task_sources.remove(i);
10946 }
10947 });
10948 }
10949 });
10950
10951 let locations = references.await?;
10952 if locations.is_empty() {
10953 return anyhow::Ok(Navigated::No);
10954 }
10955
10956 workspace.update_in(&mut cx, |workspace, window, cx| {
10957 let title = locations
10958 .first()
10959 .as_ref()
10960 .map(|location| {
10961 let buffer = location.buffer.read(cx);
10962 format!(
10963 "References to `{}`",
10964 buffer
10965 .text_for_range(location.range.clone())
10966 .collect::<String>()
10967 )
10968 })
10969 .unwrap();
10970 Self::open_locations_in_multibuffer(
10971 workspace,
10972 locations,
10973 title,
10974 false,
10975 MultibufferSelectionMode::First,
10976 window,
10977 cx,
10978 );
10979 Navigated::Yes
10980 })
10981 }))
10982 }
10983
10984 /// Opens a multibuffer with the given project locations in it
10985 pub fn open_locations_in_multibuffer(
10986 workspace: &mut Workspace,
10987 mut locations: Vec<Location>,
10988 title: String,
10989 split: bool,
10990 multibuffer_selection_mode: MultibufferSelectionMode,
10991 window: &mut Window,
10992 cx: &mut Context<Workspace>,
10993 ) {
10994 // If there are multiple definitions, open them in a multibuffer
10995 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10996 let mut locations = locations.into_iter().peekable();
10997 let mut ranges = Vec::new();
10998 let capability = workspace.project().read(cx).capability();
10999
11000 let excerpt_buffer = cx.new(|cx| {
11001 let mut multibuffer = MultiBuffer::new(capability);
11002 while let Some(location) = locations.next() {
11003 let buffer = location.buffer.read(cx);
11004 let mut ranges_for_buffer = Vec::new();
11005 let range = location.range.to_offset(buffer);
11006 ranges_for_buffer.push(range.clone());
11007
11008 while let Some(next_location) = locations.peek() {
11009 if next_location.buffer == location.buffer {
11010 ranges_for_buffer.push(next_location.range.to_offset(buffer));
11011 locations.next();
11012 } else {
11013 break;
11014 }
11015 }
11016
11017 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
11018 ranges.extend(multibuffer.push_excerpts_with_context_lines(
11019 location.buffer.clone(),
11020 ranges_for_buffer,
11021 DEFAULT_MULTIBUFFER_CONTEXT,
11022 cx,
11023 ))
11024 }
11025
11026 multibuffer.with_title(title)
11027 });
11028
11029 let editor = cx.new(|cx| {
11030 Editor::for_multibuffer(
11031 excerpt_buffer,
11032 Some(workspace.project().clone()),
11033 true,
11034 window,
11035 cx,
11036 )
11037 });
11038 editor.update(cx, |editor, cx| {
11039 match multibuffer_selection_mode {
11040 MultibufferSelectionMode::First => {
11041 if let Some(first_range) = ranges.first() {
11042 editor.change_selections(None, window, cx, |selections| {
11043 selections.clear_disjoint();
11044 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
11045 });
11046 }
11047 editor.highlight_background::<Self>(
11048 &ranges,
11049 |theme| theme.editor_highlighted_line_background,
11050 cx,
11051 );
11052 }
11053 MultibufferSelectionMode::All => {
11054 editor.change_selections(None, window, cx, |selections| {
11055 selections.clear_disjoint();
11056 selections.select_anchor_ranges(ranges);
11057 });
11058 }
11059 }
11060 editor.register_buffers_with_language_servers(cx);
11061 });
11062
11063 let item = Box::new(editor);
11064 let item_id = item.item_id();
11065
11066 if split {
11067 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
11068 } else {
11069 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
11070 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
11071 pane.close_current_preview_item(window, cx)
11072 } else {
11073 None
11074 }
11075 });
11076 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
11077 }
11078 workspace.active_pane().update(cx, |pane, cx| {
11079 pane.set_preview_item_id(Some(item_id), cx);
11080 });
11081 }
11082
11083 pub fn rename(
11084 &mut self,
11085 _: &Rename,
11086 window: &mut Window,
11087 cx: &mut Context<Self>,
11088 ) -> Option<Task<Result<()>>> {
11089 use language::ToOffset as _;
11090
11091 let provider = self.semantics_provider.clone()?;
11092 let selection = self.selections.newest_anchor().clone();
11093 let (cursor_buffer, cursor_buffer_position) = self
11094 .buffer
11095 .read(cx)
11096 .text_anchor_for_position(selection.head(), cx)?;
11097 let (tail_buffer, cursor_buffer_position_end) = self
11098 .buffer
11099 .read(cx)
11100 .text_anchor_for_position(selection.tail(), cx)?;
11101 if tail_buffer != cursor_buffer {
11102 return None;
11103 }
11104
11105 let snapshot = cursor_buffer.read(cx).snapshot();
11106 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
11107 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
11108 let prepare_rename = provider
11109 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
11110 .unwrap_or_else(|| Task::ready(Ok(None)));
11111 drop(snapshot);
11112
11113 Some(cx.spawn_in(window, |this, mut cx| async move {
11114 let rename_range = if let Some(range) = prepare_rename.await? {
11115 Some(range)
11116 } else {
11117 this.update(&mut cx, |this, cx| {
11118 let buffer = this.buffer.read(cx).snapshot(cx);
11119 let mut buffer_highlights = this
11120 .document_highlights_for_position(selection.head(), &buffer)
11121 .filter(|highlight| {
11122 highlight.start.excerpt_id == selection.head().excerpt_id
11123 && highlight.end.excerpt_id == selection.head().excerpt_id
11124 });
11125 buffer_highlights
11126 .next()
11127 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
11128 })?
11129 };
11130 if let Some(rename_range) = rename_range {
11131 this.update_in(&mut cx, |this, window, cx| {
11132 let snapshot = cursor_buffer.read(cx).snapshot();
11133 let rename_buffer_range = rename_range.to_offset(&snapshot);
11134 let cursor_offset_in_rename_range =
11135 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
11136 let cursor_offset_in_rename_range_end =
11137 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
11138
11139 this.take_rename(false, window, cx);
11140 let buffer = this.buffer.read(cx).read(cx);
11141 let cursor_offset = selection.head().to_offset(&buffer);
11142 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
11143 let rename_end = rename_start + rename_buffer_range.len();
11144 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
11145 let mut old_highlight_id = None;
11146 let old_name: Arc<str> = buffer
11147 .chunks(rename_start..rename_end, true)
11148 .map(|chunk| {
11149 if old_highlight_id.is_none() {
11150 old_highlight_id = chunk.syntax_highlight_id;
11151 }
11152 chunk.text
11153 })
11154 .collect::<String>()
11155 .into();
11156
11157 drop(buffer);
11158
11159 // Position the selection in the rename editor so that it matches the current selection.
11160 this.show_local_selections = false;
11161 let rename_editor = cx.new(|cx| {
11162 let mut editor = Editor::single_line(window, cx);
11163 editor.buffer.update(cx, |buffer, cx| {
11164 buffer.edit([(0..0, old_name.clone())], None, cx)
11165 });
11166 let rename_selection_range = match cursor_offset_in_rename_range
11167 .cmp(&cursor_offset_in_rename_range_end)
11168 {
11169 Ordering::Equal => {
11170 editor.select_all(&SelectAll, window, cx);
11171 return editor;
11172 }
11173 Ordering::Less => {
11174 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
11175 }
11176 Ordering::Greater => {
11177 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
11178 }
11179 };
11180 if rename_selection_range.end > old_name.len() {
11181 editor.select_all(&SelectAll, window, cx);
11182 } else {
11183 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11184 s.select_ranges([rename_selection_range]);
11185 });
11186 }
11187 editor
11188 });
11189 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
11190 if e == &EditorEvent::Focused {
11191 cx.emit(EditorEvent::FocusedIn)
11192 }
11193 })
11194 .detach();
11195
11196 let write_highlights =
11197 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
11198 let read_highlights =
11199 this.clear_background_highlights::<DocumentHighlightRead>(cx);
11200 let ranges = write_highlights
11201 .iter()
11202 .flat_map(|(_, ranges)| ranges.iter())
11203 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
11204 .cloned()
11205 .collect();
11206
11207 this.highlight_text::<Rename>(
11208 ranges,
11209 HighlightStyle {
11210 fade_out: Some(0.6),
11211 ..Default::default()
11212 },
11213 cx,
11214 );
11215 let rename_focus_handle = rename_editor.focus_handle(cx);
11216 window.focus(&rename_focus_handle);
11217 let block_id = this.insert_blocks(
11218 [BlockProperties {
11219 style: BlockStyle::Flex,
11220 placement: BlockPlacement::Below(range.start),
11221 height: 1,
11222 render: Arc::new({
11223 let rename_editor = rename_editor.clone();
11224 move |cx: &mut BlockContext| {
11225 let mut text_style = cx.editor_style.text.clone();
11226 if let Some(highlight_style) = old_highlight_id
11227 .and_then(|h| h.style(&cx.editor_style.syntax))
11228 {
11229 text_style = text_style.highlight(highlight_style);
11230 }
11231 div()
11232 .block_mouse_down()
11233 .pl(cx.anchor_x)
11234 .child(EditorElement::new(
11235 &rename_editor,
11236 EditorStyle {
11237 background: cx.theme().system().transparent,
11238 local_player: cx.editor_style.local_player,
11239 text: text_style,
11240 scrollbar_width: cx.editor_style.scrollbar_width,
11241 syntax: cx.editor_style.syntax.clone(),
11242 status: cx.editor_style.status.clone(),
11243 inlay_hints_style: HighlightStyle {
11244 font_weight: Some(FontWeight::BOLD),
11245 ..make_inlay_hints_style(cx.app)
11246 },
11247 inline_completion_styles: make_suggestion_styles(
11248 cx.app,
11249 ),
11250 ..EditorStyle::default()
11251 },
11252 ))
11253 .into_any_element()
11254 }
11255 }),
11256 priority: 0,
11257 }],
11258 Some(Autoscroll::fit()),
11259 cx,
11260 )[0];
11261 this.pending_rename = Some(RenameState {
11262 range,
11263 old_name,
11264 editor: rename_editor,
11265 block_id,
11266 });
11267 })?;
11268 }
11269
11270 Ok(())
11271 }))
11272 }
11273
11274 pub fn confirm_rename(
11275 &mut self,
11276 _: &ConfirmRename,
11277 window: &mut Window,
11278 cx: &mut Context<Self>,
11279 ) -> Option<Task<Result<()>>> {
11280 let rename = self.take_rename(false, window, cx)?;
11281 let workspace = self.workspace()?.downgrade();
11282 let (buffer, start) = self
11283 .buffer
11284 .read(cx)
11285 .text_anchor_for_position(rename.range.start, cx)?;
11286 let (end_buffer, _) = self
11287 .buffer
11288 .read(cx)
11289 .text_anchor_for_position(rename.range.end, cx)?;
11290 if buffer != end_buffer {
11291 return None;
11292 }
11293
11294 let old_name = rename.old_name;
11295 let new_name = rename.editor.read(cx).text(cx);
11296
11297 let rename = self.semantics_provider.as_ref()?.perform_rename(
11298 &buffer,
11299 start,
11300 new_name.clone(),
11301 cx,
11302 )?;
11303
11304 Some(cx.spawn_in(window, |editor, mut cx| async move {
11305 let project_transaction = rename.await?;
11306 Self::open_project_transaction(
11307 &editor,
11308 workspace,
11309 project_transaction,
11310 format!("Rename: {} → {}", old_name, new_name),
11311 cx.clone(),
11312 )
11313 .await?;
11314
11315 editor.update(&mut cx, |editor, cx| {
11316 editor.refresh_document_highlights(cx);
11317 })?;
11318 Ok(())
11319 }))
11320 }
11321
11322 fn take_rename(
11323 &mut self,
11324 moving_cursor: bool,
11325 window: &mut Window,
11326 cx: &mut Context<Self>,
11327 ) -> Option<RenameState> {
11328 let rename = self.pending_rename.take()?;
11329 if rename.editor.focus_handle(cx).is_focused(window) {
11330 window.focus(&self.focus_handle);
11331 }
11332
11333 self.remove_blocks(
11334 [rename.block_id].into_iter().collect(),
11335 Some(Autoscroll::fit()),
11336 cx,
11337 );
11338 self.clear_highlights::<Rename>(cx);
11339 self.show_local_selections = true;
11340
11341 if moving_cursor {
11342 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
11343 editor.selections.newest::<usize>(cx).head()
11344 });
11345
11346 // Update the selection to match the position of the selection inside
11347 // the rename editor.
11348 let snapshot = self.buffer.read(cx).read(cx);
11349 let rename_range = rename.range.to_offset(&snapshot);
11350 let cursor_in_editor = snapshot
11351 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
11352 .min(rename_range.end);
11353 drop(snapshot);
11354
11355 self.change_selections(None, window, cx, |s| {
11356 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
11357 });
11358 } else {
11359 self.refresh_document_highlights(cx);
11360 }
11361
11362 Some(rename)
11363 }
11364
11365 pub fn pending_rename(&self) -> Option<&RenameState> {
11366 self.pending_rename.as_ref()
11367 }
11368
11369 fn format(
11370 &mut self,
11371 _: &Format,
11372 window: &mut Window,
11373 cx: &mut Context<Self>,
11374 ) -> Option<Task<Result<()>>> {
11375 let project = match &self.project {
11376 Some(project) => project.clone(),
11377 None => return None,
11378 };
11379
11380 Some(self.perform_format(
11381 project,
11382 FormatTrigger::Manual,
11383 FormatTarget::Buffers,
11384 window,
11385 cx,
11386 ))
11387 }
11388
11389 fn format_selections(
11390 &mut self,
11391 _: &FormatSelections,
11392 window: &mut Window,
11393 cx: &mut Context<Self>,
11394 ) -> Option<Task<Result<()>>> {
11395 let project = match &self.project {
11396 Some(project) => project.clone(),
11397 None => return None,
11398 };
11399
11400 let ranges = self
11401 .selections
11402 .all_adjusted(cx)
11403 .into_iter()
11404 .map(|selection| selection.range())
11405 .collect_vec();
11406
11407 Some(self.perform_format(
11408 project,
11409 FormatTrigger::Manual,
11410 FormatTarget::Ranges(ranges),
11411 window,
11412 cx,
11413 ))
11414 }
11415
11416 fn perform_format(
11417 &mut self,
11418 project: Entity<Project>,
11419 trigger: FormatTrigger,
11420 target: FormatTarget,
11421 window: &mut Window,
11422 cx: &mut Context<Self>,
11423 ) -> Task<Result<()>> {
11424 let buffer = self.buffer.clone();
11425 let (buffers, target) = match target {
11426 FormatTarget::Buffers => {
11427 let mut buffers = buffer.read(cx).all_buffers();
11428 if trigger == FormatTrigger::Save {
11429 buffers.retain(|buffer| buffer.read(cx).is_dirty());
11430 }
11431 (buffers, LspFormatTarget::Buffers)
11432 }
11433 FormatTarget::Ranges(selection_ranges) => {
11434 let multi_buffer = buffer.read(cx);
11435 let snapshot = multi_buffer.read(cx);
11436 let mut buffers = HashSet::default();
11437 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
11438 BTreeMap::new();
11439 for selection_range in selection_ranges {
11440 for (buffer, buffer_range, _) in
11441 snapshot.range_to_buffer_ranges(selection_range)
11442 {
11443 let buffer_id = buffer.remote_id();
11444 let start = buffer.anchor_before(buffer_range.start);
11445 let end = buffer.anchor_after(buffer_range.end);
11446 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
11447 buffer_id_to_ranges
11448 .entry(buffer_id)
11449 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
11450 .or_insert_with(|| vec![start..end]);
11451 }
11452 }
11453 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
11454 }
11455 };
11456
11457 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
11458 let format = project.update(cx, |project, cx| {
11459 project.format(buffers, target, true, trigger, cx)
11460 });
11461
11462 cx.spawn_in(window, |_, mut cx| async move {
11463 let transaction = futures::select_biased! {
11464 () = timeout => {
11465 log::warn!("timed out waiting for formatting");
11466 None
11467 }
11468 transaction = format.log_err().fuse() => transaction,
11469 };
11470
11471 buffer
11472 .update(&mut cx, |buffer, cx| {
11473 if let Some(transaction) = transaction {
11474 if !buffer.is_singleton() {
11475 buffer.push_transaction(&transaction.0, cx);
11476 }
11477 }
11478
11479 cx.notify();
11480 })
11481 .ok();
11482
11483 Ok(())
11484 })
11485 }
11486
11487 fn restart_language_server(
11488 &mut self,
11489 _: &RestartLanguageServer,
11490 _: &mut Window,
11491 cx: &mut Context<Self>,
11492 ) {
11493 if let Some(project) = self.project.clone() {
11494 self.buffer.update(cx, |multi_buffer, cx| {
11495 project.update(cx, |project, cx| {
11496 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
11497 });
11498 })
11499 }
11500 }
11501
11502 fn cancel_language_server_work(
11503 workspace: &mut Workspace,
11504 _: &actions::CancelLanguageServerWork,
11505 _: &mut Window,
11506 cx: &mut Context<Workspace>,
11507 ) {
11508 let project = workspace.project();
11509 let buffers = workspace
11510 .active_item(cx)
11511 .and_then(|item| item.act_as::<Editor>(cx))
11512 .map_or(HashSet::default(), |editor| {
11513 editor.read(cx).buffer.read(cx).all_buffers()
11514 });
11515 project.update(cx, |project, cx| {
11516 project.cancel_language_server_work_for_buffers(buffers, cx);
11517 });
11518 }
11519
11520 fn show_character_palette(
11521 &mut self,
11522 _: &ShowCharacterPalette,
11523 window: &mut Window,
11524 _: &mut Context<Self>,
11525 ) {
11526 window.show_character_palette();
11527 }
11528
11529 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11530 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11531 let buffer = self.buffer.read(cx).snapshot(cx);
11532 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11533 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
11534 let is_valid = buffer
11535 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
11536 .any(|entry| {
11537 entry.diagnostic.is_primary
11538 && !entry.range.is_empty()
11539 && entry.range.start == primary_range_start
11540 && entry.diagnostic.message == active_diagnostics.primary_message
11541 });
11542
11543 if is_valid != active_diagnostics.is_valid {
11544 active_diagnostics.is_valid = is_valid;
11545 let mut new_styles = HashMap::default();
11546 for (block_id, diagnostic) in &active_diagnostics.blocks {
11547 new_styles.insert(
11548 *block_id,
11549 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11550 );
11551 }
11552 self.display_map.update(cx, |display_map, _cx| {
11553 display_map.replace_blocks(new_styles)
11554 });
11555 }
11556 }
11557 }
11558
11559 fn activate_diagnostics(
11560 &mut self,
11561 buffer_id: BufferId,
11562 group_id: usize,
11563 window: &mut Window,
11564 cx: &mut Context<Self>,
11565 ) {
11566 self.dismiss_diagnostics(cx);
11567 let snapshot = self.snapshot(window, cx);
11568 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11569 let buffer = self.buffer.read(cx).snapshot(cx);
11570
11571 let mut primary_range = None;
11572 let mut primary_message = None;
11573 let diagnostic_group = buffer
11574 .diagnostic_group(buffer_id, group_id)
11575 .filter_map(|entry| {
11576 let start = entry.range.start;
11577 let end = entry.range.end;
11578 if snapshot.is_line_folded(MultiBufferRow(start.row))
11579 && (start.row == end.row
11580 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11581 {
11582 return None;
11583 }
11584 if entry.diagnostic.is_primary {
11585 primary_range = Some(entry.range.clone());
11586 primary_message = Some(entry.diagnostic.message.clone());
11587 }
11588 Some(entry)
11589 })
11590 .collect::<Vec<_>>();
11591 let primary_range = primary_range?;
11592 let primary_message = primary_message?;
11593
11594 let blocks = display_map
11595 .insert_blocks(
11596 diagnostic_group.iter().map(|entry| {
11597 let diagnostic = entry.diagnostic.clone();
11598 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11599 BlockProperties {
11600 style: BlockStyle::Fixed,
11601 placement: BlockPlacement::Below(
11602 buffer.anchor_after(entry.range.start),
11603 ),
11604 height: message_height,
11605 render: diagnostic_block_renderer(diagnostic, None, true, true),
11606 priority: 0,
11607 }
11608 }),
11609 cx,
11610 )
11611 .into_iter()
11612 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11613 .collect();
11614
11615 Some(ActiveDiagnosticGroup {
11616 primary_range: buffer.anchor_before(primary_range.start)
11617 ..buffer.anchor_after(primary_range.end),
11618 primary_message,
11619 group_id,
11620 blocks,
11621 is_valid: true,
11622 })
11623 });
11624 }
11625
11626 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11627 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11628 self.display_map.update(cx, |display_map, cx| {
11629 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11630 });
11631 cx.notify();
11632 }
11633 }
11634
11635 pub fn set_selections_from_remote(
11636 &mut self,
11637 selections: Vec<Selection<Anchor>>,
11638 pending_selection: Option<Selection<Anchor>>,
11639 window: &mut Window,
11640 cx: &mut Context<Self>,
11641 ) {
11642 let old_cursor_position = self.selections.newest_anchor().head();
11643 self.selections.change_with(cx, |s| {
11644 s.select_anchors(selections);
11645 if let Some(pending_selection) = pending_selection {
11646 s.set_pending(pending_selection, SelectMode::Character);
11647 } else {
11648 s.clear_pending();
11649 }
11650 });
11651 self.selections_did_change(false, &old_cursor_position, true, window, cx);
11652 }
11653
11654 fn push_to_selection_history(&mut self) {
11655 self.selection_history.push(SelectionHistoryEntry {
11656 selections: self.selections.disjoint_anchors(),
11657 select_next_state: self.select_next_state.clone(),
11658 select_prev_state: self.select_prev_state.clone(),
11659 add_selections_state: self.add_selections_state.clone(),
11660 });
11661 }
11662
11663 pub fn transact(
11664 &mut self,
11665 window: &mut Window,
11666 cx: &mut Context<Self>,
11667 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
11668 ) -> Option<TransactionId> {
11669 self.start_transaction_at(Instant::now(), window, cx);
11670 update(self, window, cx);
11671 self.end_transaction_at(Instant::now(), cx)
11672 }
11673
11674 pub fn start_transaction_at(
11675 &mut self,
11676 now: Instant,
11677 window: &mut Window,
11678 cx: &mut Context<Self>,
11679 ) {
11680 self.end_selection(window, cx);
11681 if let Some(tx_id) = self
11682 .buffer
11683 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
11684 {
11685 self.selection_history
11686 .insert_transaction(tx_id, self.selections.disjoint_anchors());
11687 cx.emit(EditorEvent::TransactionBegun {
11688 transaction_id: tx_id,
11689 })
11690 }
11691 }
11692
11693 pub fn end_transaction_at(
11694 &mut self,
11695 now: Instant,
11696 cx: &mut Context<Self>,
11697 ) -> Option<TransactionId> {
11698 if let Some(transaction_id) = self
11699 .buffer
11700 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
11701 {
11702 if let Some((_, end_selections)) =
11703 self.selection_history.transaction_mut(transaction_id)
11704 {
11705 *end_selections = Some(self.selections.disjoint_anchors());
11706 } else {
11707 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
11708 }
11709
11710 cx.emit(EditorEvent::Edited { transaction_id });
11711 Some(transaction_id)
11712 } else {
11713 None
11714 }
11715 }
11716
11717 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
11718 if self.selection_mark_mode {
11719 self.change_selections(None, window, cx, |s| {
11720 s.move_with(|_, sel| {
11721 sel.collapse_to(sel.head(), SelectionGoal::None);
11722 });
11723 })
11724 }
11725 self.selection_mark_mode = true;
11726 cx.notify();
11727 }
11728
11729 pub fn swap_selection_ends(
11730 &mut self,
11731 _: &actions::SwapSelectionEnds,
11732 window: &mut Window,
11733 cx: &mut Context<Self>,
11734 ) {
11735 self.change_selections(None, window, cx, |s| {
11736 s.move_with(|_, sel| {
11737 if sel.start != sel.end {
11738 sel.reversed = !sel.reversed
11739 }
11740 });
11741 });
11742 self.request_autoscroll(Autoscroll::newest(), cx);
11743 cx.notify();
11744 }
11745
11746 pub fn toggle_fold(
11747 &mut self,
11748 _: &actions::ToggleFold,
11749 window: &mut Window,
11750 cx: &mut Context<Self>,
11751 ) {
11752 if self.is_singleton(cx) {
11753 let selection = self.selections.newest::<Point>(cx);
11754
11755 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11756 let range = if selection.is_empty() {
11757 let point = selection.head().to_display_point(&display_map);
11758 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11759 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11760 .to_point(&display_map);
11761 start..end
11762 } else {
11763 selection.range()
11764 };
11765 if display_map.folds_in_range(range).next().is_some() {
11766 self.unfold_lines(&Default::default(), window, cx)
11767 } else {
11768 self.fold(&Default::default(), window, cx)
11769 }
11770 } else {
11771 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11772 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11773 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11774 .map(|(snapshot, _, _)| snapshot.remote_id())
11775 .collect();
11776
11777 for buffer_id in buffer_ids {
11778 if self.is_buffer_folded(buffer_id, cx) {
11779 self.unfold_buffer(buffer_id, cx);
11780 } else {
11781 self.fold_buffer(buffer_id, cx);
11782 }
11783 }
11784 }
11785 }
11786
11787 pub fn toggle_fold_recursive(
11788 &mut self,
11789 _: &actions::ToggleFoldRecursive,
11790 window: &mut Window,
11791 cx: &mut Context<Self>,
11792 ) {
11793 let selection = self.selections.newest::<Point>(cx);
11794
11795 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11796 let range = if selection.is_empty() {
11797 let point = selection.head().to_display_point(&display_map);
11798 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11799 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11800 .to_point(&display_map);
11801 start..end
11802 } else {
11803 selection.range()
11804 };
11805 if display_map.folds_in_range(range).next().is_some() {
11806 self.unfold_recursive(&Default::default(), window, cx)
11807 } else {
11808 self.fold_recursive(&Default::default(), window, cx)
11809 }
11810 }
11811
11812 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
11813 if self.is_singleton(cx) {
11814 let mut to_fold = Vec::new();
11815 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11816 let selections = self.selections.all_adjusted(cx);
11817
11818 for selection in selections {
11819 let range = selection.range().sorted();
11820 let buffer_start_row = range.start.row;
11821
11822 if range.start.row != range.end.row {
11823 let mut found = false;
11824 let mut row = range.start.row;
11825 while row <= range.end.row {
11826 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
11827 {
11828 found = true;
11829 row = crease.range().end.row + 1;
11830 to_fold.push(crease);
11831 } else {
11832 row += 1
11833 }
11834 }
11835 if found {
11836 continue;
11837 }
11838 }
11839
11840 for row in (0..=range.start.row).rev() {
11841 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11842 if crease.range().end.row >= buffer_start_row {
11843 to_fold.push(crease);
11844 if row <= range.start.row {
11845 break;
11846 }
11847 }
11848 }
11849 }
11850 }
11851
11852 self.fold_creases(to_fold, true, window, cx);
11853 } else {
11854 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11855
11856 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11857 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11858 .map(|(snapshot, _, _)| snapshot.remote_id())
11859 .collect();
11860 for buffer_id in buffer_ids {
11861 self.fold_buffer(buffer_id, cx);
11862 }
11863 }
11864 }
11865
11866 fn fold_at_level(
11867 &mut self,
11868 fold_at: &FoldAtLevel,
11869 window: &mut Window,
11870 cx: &mut Context<Self>,
11871 ) {
11872 if !self.buffer.read(cx).is_singleton() {
11873 return;
11874 }
11875
11876 let fold_at_level = fold_at.0;
11877 let snapshot = self.buffer.read(cx).snapshot(cx);
11878 let mut to_fold = Vec::new();
11879 let mut stack = vec![(0, snapshot.max_row().0, 1)];
11880
11881 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
11882 while start_row < end_row {
11883 match self
11884 .snapshot(window, cx)
11885 .crease_for_buffer_row(MultiBufferRow(start_row))
11886 {
11887 Some(crease) => {
11888 let nested_start_row = crease.range().start.row + 1;
11889 let nested_end_row = crease.range().end.row;
11890
11891 if current_level < fold_at_level {
11892 stack.push((nested_start_row, nested_end_row, current_level + 1));
11893 } else if current_level == fold_at_level {
11894 to_fold.push(crease);
11895 }
11896
11897 start_row = nested_end_row + 1;
11898 }
11899 None => start_row += 1,
11900 }
11901 }
11902 }
11903
11904 self.fold_creases(to_fold, true, window, cx);
11905 }
11906
11907 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
11908 if self.buffer.read(cx).is_singleton() {
11909 let mut fold_ranges = Vec::new();
11910 let snapshot = self.buffer.read(cx).snapshot(cx);
11911
11912 for row in 0..snapshot.max_row().0 {
11913 if let Some(foldable_range) = self
11914 .snapshot(window, cx)
11915 .crease_for_buffer_row(MultiBufferRow(row))
11916 {
11917 fold_ranges.push(foldable_range);
11918 }
11919 }
11920
11921 self.fold_creases(fold_ranges, true, window, cx);
11922 } else {
11923 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
11924 editor
11925 .update_in(&mut cx, |editor, _, cx| {
11926 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
11927 editor.fold_buffer(buffer_id, cx);
11928 }
11929 })
11930 .ok();
11931 });
11932 }
11933 }
11934
11935 pub fn fold_function_bodies(
11936 &mut self,
11937 _: &actions::FoldFunctionBodies,
11938 window: &mut Window,
11939 cx: &mut Context<Self>,
11940 ) {
11941 let snapshot = self.buffer.read(cx).snapshot(cx);
11942
11943 let ranges = snapshot
11944 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
11945 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
11946 .collect::<Vec<_>>();
11947
11948 let creases = ranges
11949 .into_iter()
11950 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
11951 .collect();
11952
11953 self.fold_creases(creases, true, window, cx);
11954 }
11955
11956 pub fn fold_recursive(
11957 &mut self,
11958 _: &actions::FoldRecursive,
11959 window: &mut Window,
11960 cx: &mut Context<Self>,
11961 ) {
11962 let mut to_fold = Vec::new();
11963 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11964 let selections = self.selections.all_adjusted(cx);
11965
11966 for selection in selections {
11967 let range = selection.range().sorted();
11968 let buffer_start_row = range.start.row;
11969
11970 if range.start.row != range.end.row {
11971 let mut found = false;
11972 for row in range.start.row..=range.end.row {
11973 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11974 found = true;
11975 to_fold.push(crease);
11976 }
11977 }
11978 if found {
11979 continue;
11980 }
11981 }
11982
11983 for row in (0..=range.start.row).rev() {
11984 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11985 if crease.range().end.row >= buffer_start_row {
11986 to_fold.push(crease);
11987 } else {
11988 break;
11989 }
11990 }
11991 }
11992 }
11993
11994 self.fold_creases(to_fold, true, window, cx);
11995 }
11996
11997 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
11998 let buffer_row = fold_at.buffer_row;
11999 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12000
12001 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
12002 let autoscroll = self
12003 .selections
12004 .all::<Point>(cx)
12005 .iter()
12006 .any(|selection| crease.range().overlaps(&selection.range()));
12007
12008 self.fold_creases(vec![crease], autoscroll, window, cx);
12009 }
12010 }
12011
12012 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
12013 if self.is_singleton(cx) {
12014 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12015 let buffer = &display_map.buffer_snapshot;
12016 let selections = self.selections.all::<Point>(cx);
12017 let ranges = selections
12018 .iter()
12019 .map(|s| {
12020 let range = s.display_range(&display_map).sorted();
12021 let mut start = range.start.to_point(&display_map);
12022 let mut end = range.end.to_point(&display_map);
12023 start.column = 0;
12024 end.column = buffer.line_len(MultiBufferRow(end.row));
12025 start..end
12026 })
12027 .collect::<Vec<_>>();
12028
12029 self.unfold_ranges(&ranges, true, true, cx);
12030 } else {
12031 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12032 let buffer_ids: HashSet<_> = multi_buffer_snapshot
12033 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
12034 .map(|(snapshot, _, _)| snapshot.remote_id())
12035 .collect();
12036 for buffer_id in buffer_ids {
12037 self.unfold_buffer(buffer_id, cx);
12038 }
12039 }
12040 }
12041
12042 pub fn unfold_recursive(
12043 &mut self,
12044 _: &UnfoldRecursive,
12045 _window: &mut Window,
12046 cx: &mut Context<Self>,
12047 ) {
12048 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12049 let selections = self.selections.all::<Point>(cx);
12050 let ranges = selections
12051 .iter()
12052 .map(|s| {
12053 let mut range = s.display_range(&display_map).sorted();
12054 *range.start.column_mut() = 0;
12055 *range.end.column_mut() = display_map.line_len(range.end.row());
12056 let start = range.start.to_point(&display_map);
12057 let end = range.end.to_point(&display_map);
12058 start..end
12059 })
12060 .collect::<Vec<_>>();
12061
12062 self.unfold_ranges(&ranges, true, true, cx);
12063 }
12064
12065 pub fn unfold_at(
12066 &mut self,
12067 unfold_at: &UnfoldAt,
12068 _window: &mut Window,
12069 cx: &mut Context<Self>,
12070 ) {
12071 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12072
12073 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
12074 ..Point::new(
12075 unfold_at.buffer_row.0,
12076 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
12077 );
12078
12079 let autoscroll = self
12080 .selections
12081 .all::<Point>(cx)
12082 .iter()
12083 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
12084
12085 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
12086 }
12087
12088 pub fn unfold_all(
12089 &mut self,
12090 _: &actions::UnfoldAll,
12091 _window: &mut Window,
12092 cx: &mut Context<Self>,
12093 ) {
12094 if self.buffer.read(cx).is_singleton() {
12095 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12096 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
12097 } else {
12098 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
12099 editor
12100 .update(&mut cx, |editor, cx| {
12101 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
12102 editor.unfold_buffer(buffer_id, cx);
12103 }
12104 })
12105 .ok();
12106 });
12107 }
12108 }
12109
12110 pub fn fold_selected_ranges(
12111 &mut self,
12112 _: &FoldSelectedRanges,
12113 window: &mut Window,
12114 cx: &mut Context<Self>,
12115 ) {
12116 let selections = self.selections.all::<Point>(cx);
12117 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12118 let line_mode = self.selections.line_mode;
12119 let ranges = selections
12120 .into_iter()
12121 .map(|s| {
12122 if line_mode {
12123 let start = Point::new(s.start.row, 0);
12124 let end = Point::new(
12125 s.end.row,
12126 display_map
12127 .buffer_snapshot
12128 .line_len(MultiBufferRow(s.end.row)),
12129 );
12130 Crease::simple(start..end, display_map.fold_placeholder.clone())
12131 } else {
12132 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
12133 }
12134 })
12135 .collect::<Vec<_>>();
12136 self.fold_creases(ranges, true, window, cx);
12137 }
12138
12139 pub fn fold_ranges<T: ToOffset + Clone>(
12140 &mut self,
12141 ranges: Vec<Range<T>>,
12142 auto_scroll: bool,
12143 window: &mut Window,
12144 cx: &mut Context<Self>,
12145 ) {
12146 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12147 let ranges = ranges
12148 .into_iter()
12149 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
12150 .collect::<Vec<_>>();
12151 self.fold_creases(ranges, auto_scroll, window, cx);
12152 }
12153
12154 pub fn fold_creases<T: ToOffset + Clone>(
12155 &mut self,
12156 creases: Vec<Crease<T>>,
12157 auto_scroll: bool,
12158 window: &mut Window,
12159 cx: &mut Context<Self>,
12160 ) {
12161 if creases.is_empty() {
12162 return;
12163 }
12164
12165 let mut buffers_affected = HashSet::default();
12166 let multi_buffer = self.buffer().read(cx);
12167 for crease in &creases {
12168 if let Some((_, buffer, _)) =
12169 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
12170 {
12171 buffers_affected.insert(buffer.read(cx).remote_id());
12172 };
12173 }
12174
12175 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
12176
12177 if auto_scroll {
12178 self.request_autoscroll(Autoscroll::fit(), cx);
12179 }
12180
12181 cx.notify();
12182
12183 if let Some(active_diagnostics) = self.active_diagnostics.take() {
12184 // Clear diagnostics block when folding a range that contains it.
12185 let snapshot = self.snapshot(window, cx);
12186 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
12187 drop(snapshot);
12188 self.active_diagnostics = Some(active_diagnostics);
12189 self.dismiss_diagnostics(cx);
12190 } else {
12191 self.active_diagnostics = Some(active_diagnostics);
12192 }
12193 }
12194
12195 self.scrollbar_marker_state.dirty = true;
12196 }
12197
12198 /// Removes any folds whose ranges intersect any of the given ranges.
12199 pub fn unfold_ranges<T: ToOffset + Clone>(
12200 &mut self,
12201 ranges: &[Range<T>],
12202 inclusive: bool,
12203 auto_scroll: bool,
12204 cx: &mut Context<Self>,
12205 ) {
12206 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12207 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
12208 });
12209 }
12210
12211 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12212 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
12213 return;
12214 }
12215 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12216 self.display_map
12217 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
12218 cx.emit(EditorEvent::BufferFoldToggled {
12219 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
12220 folded: true,
12221 });
12222 cx.notify();
12223 }
12224
12225 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12226 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
12227 return;
12228 }
12229 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12230 self.display_map.update(cx, |display_map, cx| {
12231 display_map.unfold_buffer(buffer_id, cx);
12232 });
12233 cx.emit(EditorEvent::BufferFoldToggled {
12234 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
12235 folded: false,
12236 });
12237 cx.notify();
12238 }
12239
12240 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
12241 self.display_map.read(cx).is_buffer_folded(buffer)
12242 }
12243
12244 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
12245 self.display_map.read(cx).folded_buffers()
12246 }
12247
12248 /// Removes any folds with the given ranges.
12249 pub fn remove_folds_with_type<T: ToOffset + Clone>(
12250 &mut self,
12251 ranges: &[Range<T>],
12252 type_id: TypeId,
12253 auto_scroll: bool,
12254 cx: &mut Context<Self>,
12255 ) {
12256 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12257 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
12258 });
12259 }
12260
12261 fn remove_folds_with<T: ToOffset + Clone>(
12262 &mut self,
12263 ranges: &[Range<T>],
12264 auto_scroll: bool,
12265 cx: &mut Context<Self>,
12266 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
12267 ) {
12268 if ranges.is_empty() {
12269 return;
12270 }
12271
12272 let mut buffers_affected = HashSet::default();
12273 let multi_buffer = self.buffer().read(cx);
12274 for range in ranges {
12275 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
12276 buffers_affected.insert(buffer.read(cx).remote_id());
12277 };
12278 }
12279
12280 self.display_map.update(cx, update);
12281
12282 if auto_scroll {
12283 self.request_autoscroll(Autoscroll::fit(), cx);
12284 }
12285
12286 cx.notify();
12287 self.scrollbar_marker_state.dirty = true;
12288 self.active_indent_guides_state.dirty = true;
12289 }
12290
12291 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
12292 self.display_map.read(cx).fold_placeholder.clone()
12293 }
12294
12295 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
12296 self.buffer.update(cx, |buffer, cx| {
12297 buffer.set_all_diff_hunks_expanded(cx);
12298 });
12299 }
12300
12301 pub fn expand_all_diff_hunks(
12302 &mut self,
12303 _: &ExpandAllHunkDiffs,
12304 _window: &mut Window,
12305 cx: &mut Context<Self>,
12306 ) {
12307 self.buffer.update(cx, |buffer, cx| {
12308 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
12309 });
12310 }
12311
12312 pub fn toggle_selected_diff_hunks(
12313 &mut self,
12314 _: &ToggleSelectedDiffHunks,
12315 _window: &mut Window,
12316 cx: &mut Context<Self>,
12317 ) {
12318 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12319 self.toggle_diff_hunks_in_ranges(ranges, cx);
12320 }
12321
12322 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
12323 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12324 self.buffer
12325 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
12326 }
12327
12328 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
12329 self.buffer.update(cx, |buffer, cx| {
12330 let ranges = vec![Anchor::min()..Anchor::max()];
12331 if !buffer.all_diff_hunks_expanded()
12332 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
12333 {
12334 buffer.collapse_diff_hunks(ranges, cx);
12335 true
12336 } else {
12337 false
12338 }
12339 })
12340 }
12341
12342 fn toggle_diff_hunks_in_ranges(
12343 &mut self,
12344 ranges: Vec<Range<Anchor>>,
12345 cx: &mut Context<'_, Editor>,
12346 ) {
12347 self.buffer.update(cx, |buffer, cx| {
12348 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12349 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
12350 })
12351 }
12352
12353 fn toggle_diff_hunks_in_ranges_narrow(
12354 &mut self,
12355 ranges: Vec<Range<Anchor>>,
12356 cx: &mut Context<'_, Editor>,
12357 ) {
12358 self.buffer.update(cx, |buffer, cx| {
12359 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
12360 buffer.expand_or_collapse_diff_hunks_narrow(ranges, expand, cx);
12361 })
12362 }
12363
12364 pub(crate) fn apply_all_diff_hunks(
12365 &mut self,
12366 _: &ApplyAllDiffHunks,
12367 window: &mut Window,
12368 cx: &mut Context<Self>,
12369 ) {
12370 let buffers = self.buffer.read(cx).all_buffers();
12371 for branch_buffer in buffers {
12372 branch_buffer.update(cx, |branch_buffer, cx| {
12373 branch_buffer.merge_into_base(Vec::new(), cx);
12374 });
12375 }
12376
12377 if let Some(project) = self.project.clone() {
12378 self.save(true, project, window, cx).detach_and_log_err(cx);
12379 }
12380 }
12381
12382 pub(crate) fn apply_selected_diff_hunks(
12383 &mut self,
12384 _: &ApplyDiffHunk,
12385 window: &mut Window,
12386 cx: &mut Context<Self>,
12387 ) {
12388 let snapshot = self.snapshot(window, cx);
12389 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
12390 let mut ranges_by_buffer = HashMap::default();
12391 self.transact(window, cx, |editor, _window, cx| {
12392 for hunk in hunks {
12393 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
12394 ranges_by_buffer
12395 .entry(buffer.clone())
12396 .or_insert_with(Vec::new)
12397 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
12398 }
12399 }
12400
12401 for (buffer, ranges) in ranges_by_buffer {
12402 buffer.update(cx, |buffer, cx| {
12403 buffer.merge_into_base(ranges, cx);
12404 });
12405 }
12406 });
12407
12408 if let Some(project) = self.project.clone() {
12409 self.save(true, project, window, cx).detach_and_log_err(cx);
12410 }
12411 }
12412
12413 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
12414 if hovered != self.gutter_hovered {
12415 self.gutter_hovered = hovered;
12416 cx.notify();
12417 }
12418 }
12419
12420 pub fn insert_blocks(
12421 &mut self,
12422 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
12423 autoscroll: Option<Autoscroll>,
12424 cx: &mut Context<Self>,
12425 ) -> Vec<CustomBlockId> {
12426 let blocks = self
12427 .display_map
12428 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
12429 if let Some(autoscroll) = autoscroll {
12430 self.request_autoscroll(autoscroll, cx);
12431 }
12432 cx.notify();
12433 blocks
12434 }
12435
12436 pub fn resize_blocks(
12437 &mut self,
12438 heights: HashMap<CustomBlockId, u32>,
12439 autoscroll: Option<Autoscroll>,
12440 cx: &mut Context<Self>,
12441 ) {
12442 self.display_map
12443 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
12444 if let Some(autoscroll) = autoscroll {
12445 self.request_autoscroll(autoscroll, cx);
12446 }
12447 cx.notify();
12448 }
12449
12450 pub fn replace_blocks(
12451 &mut self,
12452 renderers: HashMap<CustomBlockId, RenderBlock>,
12453 autoscroll: Option<Autoscroll>,
12454 cx: &mut Context<Self>,
12455 ) {
12456 self.display_map
12457 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
12458 if let Some(autoscroll) = autoscroll {
12459 self.request_autoscroll(autoscroll, cx);
12460 }
12461 cx.notify();
12462 }
12463
12464 pub fn remove_blocks(
12465 &mut self,
12466 block_ids: HashSet<CustomBlockId>,
12467 autoscroll: Option<Autoscroll>,
12468 cx: &mut Context<Self>,
12469 ) {
12470 self.display_map.update(cx, |display_map, cx| {
12471 display_map.remove_blocks(block_ids, cx)
12472 });
12473 if let Some(autoscroll) = autoscroll {
12474 self.request_autoscroll(autoscroll, cx);
12475 }
12476 cx.notify();
12477 }
12478
12479 pub fn row_for_block(
12480 &self,
12481 block_id: CustomBlockId,
12482 cx: &mut Context<Self>,
12483 ) -> Option<DisplayRow> {
12484 self.display_map
12485 .update(cx, |map, cx| map.row_for_block(block_id, cx))
12486 }
12487
12488 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
12489 self.focused_block = Some(focused_block);
12490 }
12491
12492 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
12493 self.focused_block.take()
12494 }
12495
12496 pub fn insert_creases(
12497 &mut self,
12498 creases: impl IntoIterator<Item = Crease<Anchor>>,
12499 cx: &mut Context<Self>,
12500 ) -> Vec<CreaseId> {
12501 self.display_map
12502 .update(cx, |map, cx| map.insert_creases(creases, cx))
12503 }
12504
12505 pub fn remove_creases(
12506 &mut self,
12507 ids: impl IntoIterator<Item = CreaseId>,
12508 cx: &mut Context<Self>,
12509 ) {
12510 self.display_map
12511 .update(cx, |map, cx| map.remove_creases(ids, cx));
12512 }
12513
12514 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
12515 self.display_map
12516 .update(cx, |map, cx| map.snapshot(cx))
12517 .longest_row()
12518 }
12519
12520 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
12521 self.display_map
12522 .update(cx, |map, cx| map.snapshot(cx))
12523 .max_point()
12524 }
12525
12526 pub fn text(&self, cx: &App) -> String {
12527 self.buffer.read(cx).read(cx).text()
12528 }
12529
12530 pub fn is_empty(&self, cx: &App) -> bool {
12531 self.buffer.read(cx).read(cx).is_empty()
12532 }
12533
12534 pub fn text_option(&self, cx: &App) -> Option<String> {
12535 let text = self.text(cx);
12536 let text = text.trim();
12537
12538 if text.is_empty() {
12539 return None;
12540 }
12541
12542 Some(text.to_string())
12543 }
12544
12545 pub fn set_text(
12546 &mut self,
12547 text: impl Into<Arc<str>>,
12548 window: &mut Window,
12549 cx: &mut Context<Self>,
12550 ) {
12551 self.transact(window, cx, |this, _, cx| {
12552 this.buffer
12553 .read(cx)
12554 .as_singleton()
12555 .expect("you can only call set_text on editors for singleton buffers")
12556 .update(cx, |buffer, cx| buffer.set_text(text, cx));
12557 });
12558 }
12559
12560 pub fn display_text(&self, cx: &mut App) -> String {
12561 self.display_map
12562 .update(cx, |map, cx| map.snapshot(cx))
12563 .text()
12564 }
12565
12566 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
12567 let mut wrap_guides = smallvec::smallvec![];
12568
12569 if self.show_wrap_guides == Some(false) {
12570 return wrap_guides;
12571 }
12572
12573 let settings = self.buffer.read(cx).settings_at(0, cx);
12574 if settings.show_wrap_guides {
12575 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
12576 wrap_guides.push((soft_wrap as usize, true));
12577 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
12578 wrap_guides.push((soft_wrap as usize, true));
12579 }
12580 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
12581 }
12582
12583 wrap_guides
12584 }
12585
12586 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
12587 let settings = self.buffer.read(cx).settings_at(0, cx);
12588 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
12589 match mode {
12590 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
12591 SoftWrap::None
12592 }
12593 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
12594 language_settings::SoftWrap::PreferredLineLength => {
12595 SoftWrap::Column(settings.preferred_line_length)
12596 }
12597 language_settings::SoftWrap::Bounded => {
12598 SoftWrap::Bounded(settings.preferred_line_length)
12599 }
12600 }
12601 }
12602
12603 pub fn set_soft_wrap_mode(
12604 &mut self,
12605 mode: language_settings::SoftWrap,
12606
12607 cx: &mut Context<Self>,
12608 ) {
12609 self.soft_wrap_mode_override = Some(mode);
12610 cx.notify();
12611 }
12612
12613 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
12614 self.text_style_refinement = Some(style);
12615 }
12616
12617 /// called by the Element so we know what style we were most recently rendered with.
12618 pub(crate) fn set_style(
12619 &mut self,
12620 style: EditorStyle,
12621 window: &mut Window,
12622 cx: &mut Context<Self>,
12623 ) {
12624 let rem_size = window.rem_size();
12625 self.display_map.update(cx, |map, cx| {
12626 map.set_font(
12627 style.text.font(),
12628 style.text.font_size.to_pixels(rem_size),
12629 cx,
12630 )
12631 });
12632 self.style = Some(style);
12633 }
12634
12635 pub fn style(&self) -> Option<&EditorStyle> {
12636 self.style.as_ref()
12637 }
12638
12639 // Called by the element. This method is not designed to be called outside of the editor
12640 // element's layout code because it does not notify when rewrapping is computed synchronously.
12641 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
12642 self.display_map
12643 .update(cx, |map, cx| map.set_wrap_width(width, cx))
12644 }
12645
12646 pub fn set_soft_wrap(&mut self) {
12647 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
12648 }
12649
12650 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
12651 if self.soft_wrap_mode_override.is_some() {
12652 self.soft_wrap_mode_override.take();
12653 } else {
12654 let soft_wrap = match self.soft_wrap_mode(cx) {
12655 SoftWrap::GitDiff => return,
12656 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
12657 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
12658 language_settings::SoftWrap::None
12659 }
12660 };
12661 self.soft_wrap_mode_override = Some(soft_wrap);
12662 }
12663 cx.notify();
12664 }
12665
12666 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
12667 let Some(workspace) = self.workspace() else {
12668 return;
12669 };
12670 let fs = workspace.read(cx).app_state().fs.clone();
12671 let current_show = TabBarSettings::get_global(cx).show;
12672 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
12673 setting.show = Some(!current_show);
12674 });
12675 }
12676
12677 pub fn toggle_indent_guides(
12678 &mut self,
12679 _: &ToggleIndentGuides,
12680 _: &mut Window,
12681 cx: &mut Context<Self>,
12682 ) {
12683 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
12684 self.buffer
12685 .read(cx)
12686 .settings_at(0, cx)
12687 .indent_guides
12688 .enabled
12689 });
12690 self.show_indent_guides = Some(!currently_enabled);
12691 cx.notify();
12692 }
12693
12694 fn should_show_indent_guides(&self) -> Option<bool> {
12695 self.show_indent_guides
12696 }
12697
12698 pub fn toggle_line_numbers(
12699 &mut self,
12700 _: &ToggleLineNumbers,
12701 _: &mut Window,
12702 cx: &mut Context<Self>,
12703 ) {
12704 let mut editor_settings = EditorSettings::get_global(cx).clone();
12705 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
12706 EditorSettings::override_global(editor_settings, cx);
12707 }
12708
12709 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
12710 self.use_relative_line_numbers
12711 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
12712 }
12713
12714 pub fn toggle_relative_line_numbers(
12715 &mut self,
12716 _: &ToggleRelativeLineNumbers,
12717 _: &mut Window,
12718 cx: &mut Context<Self>,
12719 ) {
12720 let is_relative = self.should_use_relative_line_numbers(cx);
12721 self.set_relative_line_number(Some(!is_relative), cx)
12722 }
12723
12724 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
12725 self.use_relative_line_numbers = is_relative;
12726 cx.notify();
12727 }
12728
12729 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
12730 self.show_gutter = show_gutter;
12731 cx.notify();
12732 }
12733
12734 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
12735 self.show_scrollbars = show_scrollbars;
12736 cx.notify();
12737 }
12738
12739 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
12740 self.show_line_numbers = Some(show_line_numbers);
12741 cx.notify();
12742 }
12743
12744 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
12745 self.show_git_diff_gutter = Some(show_git_diff_gutter);
12746 cx.notify();
12747 }
12748
12749 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
12750 self.show_code_actions = Some(show_code_actions);
12751 cx.notify();
12752 }
12753
12754 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
12755 self.show_runnables = Some(show_runnables);
12756 cx.notify();
12757 }
12758
12759 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
12760 if self.display_map.read(cx).masked != masked {
12761 self.display_map.update(cx, |map, _| map.masked = masked);
12762 }
12763 cx.notify()
12764 }
12765
12766 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
12767 self.show_wrap_guides = Some(show_wrap_guides);
12768 cx.notify();
12769 }
12770
12771 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
12772 self.show_indent_guides = Some(show_indent_guides);
12773 cx.notify();
12774 }
12775
12776 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
12777 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
12778 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
12779 if let Some(dir) = file.abs_path(cx).parent() {
12780 return Some(dir.to_owned());
12781 }
12782 }
12783
12784 if let Some(project_path) = buffer.read(cx).project_path(cx) {
12785 return Some(project_path.path.to_path_buf());
12786 }
12787 }
12788
12789 None
12790 }
12791
12792 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
12793 self.active_excerpt(cx)?
12794 .1
12795 .read(cx)
12796 .file()
12797 .and_then(|f| f.as_local())
12798 }
12799
12800 fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
12801 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
12802 let project_path = buffer.read(cx).project_path(cx)?;
12803 let project = self.project.as_ref()?.read(cx);
12804 project.absolute_path(&project_path, cx)
12805 })
12806 }
12807
12808 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
12809 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
12810 let project_path = buffer.read(cx).project_path(cx)?;
12811 let project = self.project.as_ref()?.read(cx);
12812 let entry = project.entry_for_path(&project_path, cx)?;
12813 let path = entry.path.to_path_buf();
12814 Some(path)
12815 })
12816 }
12817
12818 pub fn reveal_in_finder(
12819 &mut self,
12820 _: &RevealInFileManager,
12821 _window: &mut Window,
12822 cx: &mut Context<Self>,
12823 ) {
12824 if let Some(target) = self.target_file(cx) {
12825 cx.reveal_path(&target.abs_path(cx));
12826 }
12827 }
12828
12829 pub fn copy_path(&mut self, _: &CopyPath, _window: &mut Window, cx: &mut Context<Self>) {
12830 if let Some(path) = self.target_file_abs_path(cx) {
12831 if let Some(path) = path.to_str() {
12832 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
12833 }
12834 }
12835 }
12836
12837 pub fn copy_relative_path(
12838 &mut self,
12839 _: &CopyRelativePath,
12840 _window: &mut Window,
12841 cx: &mut Context<Self>,
12842 ) {
12843 if let Some(path) = self.target_file_path(cx) {
12844 if let Some(path) = path.to_str() {
12845 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
12846 }
12847 }
12848 }
12849
12850 pub fn toggle_git_blame(
12851 &mut self,
12852 _: &ToggleGitBlame,
12853 window: &mut Window,
12854 cx: &mut Context<Self>,
12855 ) {
12856 self.show_git_blame_gutter = !self.show_git_blame_gutter;
12857
12858 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
12859 self.start_git_blame(true, window, cx);
12860 }
12861
12862 cx.notify();
12863 }
12864
12865 pub fn toggle_git_blame_inline(
12866 &mut self,
12867 _: &ToggleGitBlameInline,
12868 window: &mut Window,
12869 cx: &mut Context<Self>,
12870 ) {
12871 self.toggle_git_blame_inline_internal(true, window, cx);
12872 cx.notify();
12873 }
12874
12875 pub fn git_blame_inline_enabled(&self) -> bool {
12876 self.git_blame_inline_enabled
12877 }
12878
12879 pub fn toggle_selection_menu(
12880 &mut self,
12881 _: &ToggleSelectionMenu,
12882 _: &mut Window,
12883 cx: &mut Context<Self>,
12884 ) {
12885 self.show_selection_menu = self
12886 .show_selection_menu
12887 .map(|show_selections_menu| !show_selections_menu)
12888 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
12889
12890 cx.notify();
12891 }
12892
12893 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
12894 self.show_selection_menu
12895 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
12896 }
12897
12898 fn start_git_blame(
12899 &mut self,
12900 user_triggered: bool,
12901 window: &mut Window,
12902 cx: &mut Context<Self>,
12903 ) {
12904 if let Some(project) = self.project.as_ref() {
12905 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
12906 return;
12907 };
12908
12909 if buffer.read(cx).file().is_none() {
12910 return;
12911 }
12912
12913 let focused = self.focus_handle(cx).contains_focused(window, cx);
12914
12915 let project = project.clone();
12916 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
12917 self.blame_subscription =
12918 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
12919 self.blame = Some(blame);
12920 }
12921 }
12922
12923 fn toggle_git_blame_inline_internal(
12924 &mut self,
12925 user_triggered: bool,
12926 window: &mut Window,
12927 cx: &mut Context<Self>,
12928 ) {
12929 if self.git_blame_inline_enabled {
12930 self.git_blame_inline_enabled = false;
12931 self.show_git_blame_inline = false;
12932 self.show_git_blame_inline_delay_task.take();
12933 } else {
12934 self.git_blame_inline_enabled = true;
12935 self.start_git_blame_inline(user_triggered, window, cx);
12936 }
12937
12938 cx.notify();
12939 }
12940
12941 fn start_git_blame_inline(
12942 &mut self,
12943 user_triggered: bool,
12944 window: &mut Window,
12945 cx: &mut Context<Self>,
12946 ) {
12947 self.start_git_blame(user_triggered, window, cx);
12948
12949 if ProjectSettings::get_global(cx)
12950 .git
12951 .inline_blame_delay()
12952 .is_some()
12953 {
12954 self.start_inline_blame_timer(window, cx);
12955 } else {
12956 self.show_git_blame_inline = true
12957 }
12958 }
12959
12960 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
12961 self.blame.as_ref()
12962 }
12963
12964 pub fn show_git_blame_gutter(&self) -> bool {
12965 self.show_git_blame_gutter
12966 }
12967
12968 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
12969 self.show_git_blame_gutter && self.has_blame_entries(cx)
12970 }
12971
12972 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
12973 self.show_git_blame_inline
12974 && self.focus_handle.is_focused(window)
12975 && !self.newest_selection_head_on_empty_line(cx)
12976 && self.has_blame_entries(cx)
12977 }
12978
12979 fn has_blame_entries(&self, cx: &App) -> bool {
12980 self.blame()
12981 .map_or(false, |blame| blame.read(cx).has_generated_entries())
12982 }
12983
12984 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
12985 let cursor_anchor = self.selections.newest_anchor().head();
12986
12987 let snapshot = self.buffer.read(cx).snapshot(cx);
12988 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
12989
12990 snapshot.line_len(buffer_row) == 0
12991 }
12992
12993 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
12994 let buffer_and_selection = maybe!({
12995 let selection = self.selections.newest::<Point>(cx);
12996 let selection_range = selection.range();
12997
12998 let multi_buffer = self.buffer().read(cx);
12999 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13000 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
13001
13002 let (buffer, range, _) = if selection.reversed {
13003 buffer_ranges.first()
13004 } else {
13005 buffer_ranges.last()
13006 }?;
13007
13008 let selection = text::ToPoint::to_point(&range.start, &buffer).row
13009 ..text::ToPoint::to_point(&range.end, &buffer).row;
13010 Some((
13011 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
13012 selection,
13013 ))
13014 });
13015
13016 let Some((buffer, selection)) = buffer_and_selection else {
13017 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
13018 };
13019
13020 let Some(project) = self.project.as_ref() else {
13021 return Task::ready(Err(anyhow!("editor does not have project")));
13022 };
13023
13024 project.update(cx, |project, cx| {
13025 project.get_permalink_to_line(&buffer, selection, cx)
13026 })
13027 }
13028
13029 pub fn copy_permalink_to_line(
13030 &mut self,
13031 _: &CopyPermalinkToLine,
13032 window: &mut Window,
13033 cx: &mut Context<Self>,
13034 ) {
13035 let permalink_task = self.get_permalink_to_line(cx);
13036 let workspace = self.workspace();
13037
13038 cx.spawn_in(window, |_, mut cx| async move {
13039 match permalink_task.await {
13040 Ok(permalink) => {
13041 cx.update(|_, cx| {
13042 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
13043 })
13044 .ok();
13045 }
13046 Err(err) => {
13047 let message = format!("Failed to copy permalink: {err}");
13048
13049 Err::<(), anyhow::Error>(err).log_err();
13050
13051 if let Some(workspace) = workspace {
13052 workspace
13053 .update_in(&mut cx, |workspace, _, cx| {
13054 struct CopyPermalinkToLine;
13055
13056 workspace.show_toast(
13057 Toast::new(
13058 NotificationId::unique::<CopyPermalinkToLine>(),
13059 message,
13060 ),
13061 cx,
13062 )
13063 })
13064 .ok();
13065 }
13066 }
13067 }
13068 })
13069 .detach();
13070 }
13071
13072 pub fn copy_file_location(
13073 &mut self,
13074 _: &CopyFileLocation,
13075 _: &mut Window,
13076 cx: &mut Context<Self>,
13077 ) {
13078 let selection = self.selections.newest::<Point>(cx).start.row + 1;
13079 if let Some(file) = self.target_file(cx) {
13080 if let Some(path) = file.path().to_str() {
13081 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
13082 }
13083 }
13084 }
13085
13086 pub fn open_permalink_to_line(
13087 &mut self,
13088 _: &OpenPermalinkToLine,
13089 window: &mut Window,
13090 cx: &mut Context<Self>,
13091 ) {
13092 let permalink_task = self.get_permalink_to_line(cx);
13093 let workspace = self.workspace();
13094
13095 cx.spawn_in(window, |_, mut cx| async move {
13096 match permalink_task.await {
13097 Ok(permalink) => {
13098 cx.update(|_, cx| {
13099 cx.open_url(permalink.as_ref());
13100 })
13101 .ok();
13102 }
13103 Err(err) => {
13104 let message = format!("Failed to open permalink: {err}");
13105
13106 Err::<(), anyhow::Error>(err).log_err();
13107
13108 if let Some(workspace) = workspace {
13109 workspace
13110 .update(&mut cx, |workspace, cx| {
13111 struct OpenPermalinkToLine;
13112
13113 workspace.show_toast(
13114 Toast::new(
13115 NotificationId::unique::<OpenPermalinkToLine>(),
13116 message,
13117 ),
13118 cx,
13119 )
13120 })
13121 .ok();
13122 }
13123 }
13124 }
13125 })
13126 .detach();
13127 }
13128
13129 pub fn insert_uuid_v4(
13130 &mut self,
13131 _: &InsertUuidV4,
13132 window: &mut Window,
13133 cx: &mut Context<Self>,
13134 ) {
13135 self.insert_uuid(UuidVersion::V4, window, cx);
13136 }
13137
13138 pub fn insert_uuid_v7(
13139 &mut self,
13140 _: &InsertUuidV7,
13141 window: &mut Window,
13142 cx: &mut Context<Self>,
13143 ) {
13144 self.insert_uuid(UuidVersion::V7, window, cx);
13145 }
13146
13147 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
13148 self.transact(window, cx, |this, window, cx| {
13149 let edits = this
13150 .selections
13151 .all::<Point>(cx)
13152 .into_iter()
13153 .map(|selection| {
13154 let uuid = match version {
13155 UuidVersion::V4 => uuid::Uuid::new_v4(),
13156 UuidVersion::V7 => uuid::Uuid::now_v7(),
13157 };
13158
13159 (selection.range(), uuid.to_string())
13160 });
13161 this.edit(edits, cx);
13162 this.refresh_inline_completion(true, false, window, cx);
13163 });
13164 }
13165
13166 pub fn open_selections_in_multibuffer(
13167 &mut self,
13168 _: &OpenSelectionsInMultibuffer,
13169 window: &mut Window,
13170 cx: &mut Context<Self>,
13171 ) {
13172 let multibuffer = self.buffer.read(cx);
13173
13174 let Some(buffer) = multibuffer.as_singleton() else {
13175 return;
13176 };
13177
13178 let Some(workspace) = self.workspace() else {
13179 return;
13180 };
13181
13182 let locations = self
13183 .selections
13184 .disjoint_anchors()
13185 .iter()
13186 .map(|range| Location {
13187 buffer: buffer.clone(),
13188 range: range.start.text_anchor..range.end.text_anchor,
13189 })
13190 .collect::<Vec<_>>();
13191
13192 let title = multibuffer.title(cx).to_string();
13193
13194 cx.spawn_in(window, |_, mut cx| async move {
13195 workspace.update_in(&mut cx, |workspace, window, cx| {
13196 Self::open_locations_in_multibuffer(
13197 workspace,
13198 locations,
13199 format!("Selections for '{title}'"),
13200 false,
13201 MultibufferSelectionMode::All,
13202 window,
13203 cx,
13204 );
13205 })
13206 })
13207 .detach();
13208 }
13209
13210 /// Adds a row highlight for the given range. If a row has multiple highlights, the
13211 /// last highlight added will be used.
13212 ///
13213 /// If the range ends at the beginning of a line, then that line will not be highlighted.
13214 pub fn highlight_rows<T: 'static>(
13215 &mut self,
13216 range: Range<Anchor>,
13217 color: Hsla,
13218 should_autoscroll: bool,
13219 cx: &mut Context<Self>,
13220 ) {
13221 let snapshot = self.buffer().read(cx).snapshot(cx);
13222 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13223 let ix = row_highlights.binary_search_by(|highlight| {
13224 Ordering::Equal
13225 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
13226 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
13227 });
13228
13229 if let Err(mut ix) = ix {
13230 let index = post_inc(&mut self.highlight_order);
13231
13232 // If this range intersects with the preceding highlight, then merge it with
13233 // the preceding highlight. Otherwise insert a new highlight.
13234 let mut merged = false;
13235 if ix > 0 {
13236 let prev_highlight = &mut row_highlights[ix - 1];
13237 if prev_highlight
13238 .range
13239 .end
13240 .cmp(&range.start, &snapshot)
13241 .is_ge()
13242 {
13243 ix -= 1;
13244 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
13245 prev_highlight.range.end = range.end;
13246 }
13247 merged = true;
13248 prev_highlight.index = index;
13249 prev_highlight.color = color;
13250 prev_highlight.should_autoscroll = should_autoscroll;
13251 }
13252 }
13253
13254 if !merged {
13255 row_highlights.insert(
13256 ix,
13257 RowHighlight {
13258 range: range.clone(),
13259 index,
13260 color,
13261 should_autoscroll,
13262 },
13263 );
13264 }
13265
13266 // If any of the following highlights intersect with this one, merge them.
13267 while let Some(next_highlight) = row_highlights.get(ix + 1) {
13268 let highlight = &row_highlights[ix];
13269 if next_highlight
13270 .range
13271 .start
13272 .cmp(&highlight.range.end, &snapshot)
13273 .is_le()
13274 {
13275 if next_highlight
13276 .range
13277 .end
13278 .cmp(&highlight.range.end, &snapshot)
13279 .is_gt()
13280 {
13281 row_highlights[ix].range.end = next_highlight.range.end;
13282 }
13283 row_highlights.remove(ix + 1);
13284 } else {
13285 break;
13286 }
13287 }
13288 }
13289 }
13290
13291 /// Remove any highlighted row ranges of the given type that intersect the
13292 /// given ranges.
13293 pub fn remove_highlighted_rows<T: 'static>(
13294 &mut self,
13295 ranges_to_remove: Vec<Range<Anchor>>,
13296 cx: &mut Context<Self>,
13297 ) {
13298 let snapshot = self.buffer().read(cx).snapshot(cx);
13299 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13300 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
13301 row_highlights.retain(|highlight| {
13302 while let Some(range_to_remove) = ranges_to_remove.peek() {
13303 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
13304 Ordering::Less | Ordering::Equal => {
13305 ranges_to_remove.next();
13306 }
13307 Ordering::Greater => {
13308 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
13309 Ordering::Less | Ordering::Equal => {
13310 return false;
13311 }
13312 Ordering::Greater => break,
13313 }
13314 }
13315 }
13316 }
13317
13318 true
13319 })
13320 }
13321
13322 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
13323 pub fn clear_row_highlights<T: 'static>(&mut self) {
13324 self.highlighted_rows.remove(&TypeId::of::<T>());
13325 }
13326
13327 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
13328 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
13329 self.highlighted_rows
13330 .get(&TypeId::of::<T>())
13331 .map_or(&[] as &[_], |vec| vec.as_slice())
13332 .iter()
13333 .map(|highlight| (highlight.range.clone(), highlight.color))
13334 }
13335
13336 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
13337 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
13338 /// Allows to ignore certain kinds of highlights.
13339 pub fn highlighted_display_rows(
13340 &self,
13341 window: &mut Window,
13342 cx: &mut App,
13343 ) -> BTreeMap<DisplayRow, Hsla> {
13344 let snapshot = self.snapshot(window, cx);
13345 let mut used_highlight_orders = HashMap::default();
13346 self.highlighted_rows
13347 .iter()
13348 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
13349 .fold(
13350 BTreeMap::<DisplayRow, Hsla>::new(),
13351 |mut unique_rows, highlight| {
13352 let start = highlight.range.start.to_display_point(&snapshot);
13353 let end = highlight.range.end.to_display_point(&snapshot);
13354 let start_row = start.row().0;
13355 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
13356 && end.column() == 0
13357 {
13358 end.row().0.saturating_sub(1)
13359 } else {
13360 end.row().0
13361 };
13362 for row in start_row..=end_row {
13363 let used_index =
13364 used_highlight_orders.entry(row).or_insert(highlight.index);
13365 if highlight.index >= *used_index {
13366 *used_index = highlight.index;
13367 unique_rows.insert(DisplayRow(row), highlight.color);
13368 }
13369 }
13370 unique_rows
13371 },
13372 )
13373 }
13374
13375 pub fn highlighted_display_row_for_autoscroll(
13376 &self,
13377 snapshot: &DisplaySnapshot,
13378 ) -> Option<DisplayRow> {
13379 self.highlighted_rows
13380 .values()
13381 .flat_map(|highlighted_rows| highlighted_rows.iter())
13382 .filter_map(|highlight| {
13383 if highlight.should_autoscroll {
13384 Some(highlight.range.start.to_display_point(snapshot).row())
13385 } else {
13386 None
13387 }
13388 })
13389 .min()
13390 }
13391
13392 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
13393 self.highlight_background::<SearchWithinRange>(
13394 ranges,
13395 |colors| colors.editor_document_highlight_read_background,
13396 cx,
13397 )
13398 }
13399
13400 pub fn set_breadcrumb_header(&mut self, new_header: String) {
13401 self.breadcrumb_header = Some(new_header);
13402 }
13403
13404 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
13405 self.clear_background_highlights::<SearchWithinRange>(cx);
13406 }
13407
13408 pub fn highlight_background<T: 'static>(
13409 &mut self,
13410 ranges: &[Range<Anchor>],
13411 color_fetcher: fn(&ThemeColors) -> Hsla,
13412 cx: &mut Context<Self>,
13413 ) {
13414 self.background_highlights
13415 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13416 self.scrollbar_marker_state.dirty = true;
13417 cx.notify();
13418 }
13419
13420 pub fn clear_background_highlights<T: 'static>(
13421 &mut self,
13422 cx: &mut Context<Self>,
13423 ) -> Option<BackgroundHighlight> {
13424 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
13425 if !text_highlights.1.is_empty() {
13426 self.scrollbar_marker_state.dirty = true;
13427 cx.notify();
13428 }
13429 Some(text_highlights)
13430 }
13431
13432 pub fn highlight_gutter<T: 'static>(
13433 &mut self,
13434 ranges: &[Range<Anchor>],
13435 color_fetcher: fn(&App) -> Hsla,
13436 cx: &mut Context<Self>,
13437 ) {
13438 self.gutter_highlights
13439 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13440 cx.notify();
13441 }
13442
13443 pub fn clear_gutter_highlights<T: 'static>(
13444 &mut self,
13445 cx: &mut Context<Self>,
13446 ) -> Option<GutterHighlight> {
13447 cx.notify();
13448 self.gutter_highlights.remove(&TypeId::of::<T>())
13449 }
13450
13451 #[cfg(feature = "test-support")]
13452 pub fn all_text_background_highlights(
13453 &self,
13454 window: &mut Window,
13455 cx: &mut Context<Self>,
13456 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13457 let snapshot = self.snapshot(window, cx);
13458 let buffer = &snapshot.buffer_snapshot;
13459 let start = buffer.anchor_before(0);
13460 let end = buffer.anchor_after(buffer.len());
13461 let theme = cx.theme().colors();
13462 self.background_highlights_in_range(start..end, &snapshot, theme)
13463 }
13464
13465 #[cfg(feature = "test-support")]
13466 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
13467 let snapshot = self.buffer().read(cx).snapshot(cx);
13468
13469 let highlights = self
13470 .background_highlights
13471 .get(&TypeId::of::<items::BufferSearchHighlights>());
13472
13473 if let Some((_color, ranges)) = highlights {
13474 ranges
13475 .iter()
13476 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
13477 .collect_vec()
13478 } else {
13479 vec![]
13480 }
13481 }
13482
13483 fn document_highlights_for_position<'a>(
13484 &'a self,
13485 position: Anchor,
13486 buffer: &'a MultiBufferSnapshot,
13487 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
13488 let read_highlights = self
13489 .background_highlights
13490 .get(&TypeId::of::<DocumentHighlightRead>())
13491 .map(|h| &h.1);
13492 let write_highlights = self
13493 .background_highlights
13494 .get(&TypeId::of::<DocumentHighlightWrite>())
13495 .map(|h| &h.1);
13496 let left_position = position.bias_left(buffer);
13497 let right_position = position.bias_right(buffer);
13498 read_highlights
13499 .into_iter()
13500 .chain(write_highlights)
13501 .flat_map(move |ranges| {
13502 let start_ix = match ranges.binary_search_by(|probe| {
13503 let cmp = probe.end.cmp(&left_position, buffer);
13504 if cmp.is_ge() {
13505 Ordering::Greater
13506 } else {
13507 Ordering::Less
13508 }
13509 }) {
13510 Ok(i) | Err(i) => i,
13511 };
13512
13513 ranges[start_ix..]
13514 .iter()
13515 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
13516 })
13517 }
13518
13519 pub fn has_background_highlights<T: 'static>(&self) -> bool {
13520 self.background_highlights
13521 .get(&TypeId::of::<T>())
13522 .map_or(false, |(_, highlights)| !highlights.is_empty())
13523 }
13524
13525 pub fn background_highlights_in_range(
13526 &self,
13527 search_range: Range<Anchor>,
13528 display_snapshot: &DisplaySnapshot,
13529 theme: &ThemeColors,
13530 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13531 let mut results = Vec::new();
13532 for (color_fetcher, ranges) in self.background_highlights.values() {
13533 let color = color_fetcher(theme);
13534 let start_ix = match ranges.binary_search_by(|probe| {
13535 let cmp = probe
13536 .end
13537 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13538 if cmp.is_gt() {
13539 Ordering::Greater
13540 } else {
13541 Ordering::Less
13542 }
13543 }) {
13544 Ok(i) | Err(i) => i,
13545 };
13546 for range in &ranges[start_ix..] {
13547 if range
13548 .start
13549 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13550 .is_ge()
13551 {
13552 break;
13553 }
13554
13555 let start = range.start.to_display_point(display_snapshot);
13556 let end = range.end.to_display_point(display_snapshot);
13557 results.push((start..end, color))
13558 }
13559 }
13560 results
13561 }
13562
13563 pub fn background_highlight_row_ranges<T: 'static>(
13564 &self,
13565 search_range: Range<Anchor>,
13566 display_snapshot: &DisplaySnapshot,
13567 count: usize,
13568 ) -> Vec<RangeInclusive<DisplayPoint>> {
13569 let mut results = Vec::new();
13570 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
13571 return vec![];
13572 };
13573
13574 let start_ix = match ranges.binary_search_by(|probe| {
13575 let cmp = probe
13576 .end
13577 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13578 if cmp.is_gt() {
13579 Ordering::Greater
13580 } else {
13581 Ordering::Less
13582 }
13583 }) {
13584 Ok(i) | Err(i) => i,
13585 };
13586 let mut push_region = |start: Option<Point>, end: Option<Point>| {
13587 if let (Some(start_display), Some(end_display)) = (start, end) {
13588 results.push(
13589 start_display.to_display_point(display_snapshot)
13590 ..=end_display.to_display_point(display_snapshot),
13591 );
13592 }
13593 };
13594 let mut start_row: Option<Point> = None;
13595 let mut end_row: Option<Point> = None;
13596 if ranges.len() > count {
13597 return Vec::new();
13598 }
13599 for range in &ranges[start_ix..] {
13600 if range
13601 .start
13602 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13603 .is_ge()
13604 {
13605 break;
13606 }
13607 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
13608 if let Some(current_row) = &end_row {
13609 if end.row == current_row.row {
13610 continue;
13611 }
13612 }
13613 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
13614 if start_row.is_none() {
13615 assert_eq!(end_row, None);
13616 start_row = Some(start);
13617 end_row = Some(end);
13618 continue;
13619 }
13620 if let Some(current_end) = end_row.as_mut() {
13621 if start.row > current_end.row + 1 {
13622 push_region(start_row, end_row);
13623 start_row = Some(start);
13624 end_row = Some(end);
13625 } else {
13626 // Merge two hunks.
13627 *current_end = end;
13628 }
13629 } else {
13630 unreachable!();
13631 }
13632 }
13633 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
13634 push_region(start_row, end_row);
13635 results
13636 }
13637
13638 pub fn gutter_highlights_in_range(
13639 &self,
13640 search_range: Range<Anchor>,
13641 display_snapshot: &DisplaySnapshot,
13642 cx: &App,
13643 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13644 let mut results = Vec::new();
13645 for (color_fetcher, ranges) in self.gutter_highlights.values() {
13646 let color = color_fetcher(cx);
13647 let start_ix = match ranges.binary_search_by(|probe| {
13648 let cmp = probe
13649 .end
13650 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13651 if cmp.is_gt() {
13652 Ordering::Greater
13653 } else {
13654 Ordering::Less
13655 }
13656 }) {
13657 Ok(i) | Err(i) => i,
13658 };
13659 for range in &ranges[start_ix..] {
13660 if range
13661 .start
13662 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13663 .is_ge()
13664 {
13665 break;
13666 }
13667
13668 let start = range.start.to_display_point(display_snapshot);
13669 let end = range.end.to_display_point(display_snapshot);
13670 results.push((start..end, color))
13671 }
13672 }
13673 results
13674 }
13675
13676 /// Get the text ranges corresponding to the redaction query
13677 pub fn redacted_ranges(
13678 &self,
13679 search_range: Range<Anchor>,
13680 display_snapshot: &DisplaySnapshot,
13681 cx: &App,
13682 ) -> Vec<Range<DisplayPoint>> {
13683 display_snapshot
13684 .buffer_snapshot
13685 .redacted_ranges(search_range, |file| {
13686 if let Some(file) = file {
13687 file.is_private()
13688 && EditorSettings::get(
13689 Some(SettingsLocation {
13690 worktree_id: file.worktree_id(cx),
13691 path: file.path().as_ref(),
13692 }),
13693 cx,
13694 )
13695 .redact_private_values
13696 } else {
13697 false
13698 }
13699 })
13700 .map(|range| {
13701 range.start.to_display_point(display_snapshot)
13702 ..range.end.to_display_point(display_snapshot)
13703 })
13704 .collect()
13705 }
13706
13707 pub fn highlight_text<T: 'static>(
13708 &mut self,
13709 ranges: Vec<Range<Anchor>>,
13710 style: HighlightStyle,
13711 cx: &mut Context<Self>,
13712 ) {
13713 self.display_map.update(cx, |map, _| {
13714 map.highlight_text(TypeId::of::<T>(), ranges, style)
13715 });
13716 cx.notify();
13717 }
13718
13719 pub(crate) fn highlight_inlays<T: 'static>(
13720 &mut self,
13721 highlights: Vec<InlayHighlight>,
13722 style: HighlightStyle,
13723 cx: &mut Context<Self>,
13724 ) {
13725 self.display_map.update(cx, |map, _| {
13726 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
13727 });
13728 cx.notify();
13729 }
13730
13731 pub fn text_highlights<'a, T: 'static>(
13732 &'a self,
13733 cx: &'a App,
13734 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
13735 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
13736 }
13737
13738 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
13739 let cleared = self
13740 .display_map
13741 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
13742 if cleared {
13743 cx.notify();
13744 }
13745 }
13746
13747 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
13748 (self.read_only(cx) || self.blink_manager.read(cx).visible())
13749 && self.focus_handle.is_focused(window)
13750 }
13751
13752 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
13753 self.show_cursor_when_unfocused = is_enabled;
13754 cx.notify();
13755 }
13756
13757 pub fn lsp_store(&self, cx: &App) -> Option<Entity<LspStore>> {
13758 self.project
13759 .as_ref()
13760 .map(|project| project.read(cx).lsp_store())
13761 }
13762
13763 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
13764 cx.notify();
13765 }
13766
13767 fn on_buffer_event(
13768 &mut self,
13769 multibuffer: &Entity<MultiBuffer>,
13770 event: &multi_buffer::Event,
13771 window: &mut Window,
13772 cx: &mut Context<Self>,
13773 ) {
13774 match event {
13775 multi_buffer::Event::Edited {
13776 singleton_buffer_edited,
13777 edited_buffer: buffer_edited,
13778 } => {
13779 self.scrollbar_marker_state.dirty = true;
13780 self.active_indent_guides_state.dirty = true;
13781 self.refresh_active_diagnostics(cx);
13782 self.refresh_code_actions(window, cx);
13783 if self.has_active_inline_completion() {
13784 self.update_visible_inline_completion(window, cx);
13785 }
13786 if let Some(buffer) = buffer_edited {
13787 let buffer_id = buffer.read(cx).remote_id();
13788 if !self.registered_buffers.contains_key(&buffer_id) {
13789 if let Some(lsp_store) = self.lsp_store(cx) {
13790 lsp_store.update(cx, |lsp_store, cx| {
13791 self.registered_buffers.insert(
13792 buffer_id,
13793 lsp_store.register_buffer_with_language_servers(&buffer, cx),
13794 );
13795 })
13796 }
13797 }
13798 }
13799 cx.emit(EditorEvent::BufferEdited);
13800 cx.emit(SearchEvent::MatchesInvalidated);
13801 if *singleton_buffer_edited {
13802 if let Some(project) = &self.project {
13803 let project = project.read(cx);
13804 #[allow(clippy::mutable_key_type)]
13805 let languages_affected = multibuffer
13806 .read(cx)
13807 .all_buffers()
13808 .into_iter()
13809 .filter_map(|buffer| {
13810 let buffer = buffer.read(cx);
13811 let language = buffer.language()?;
13812 if project.is_local()
13813 && project
13814 .language_servers_for_local_buffer(buffer, cx)
13815 .count()
13816 == 0
13817 {
13818 None
13819 } else {
13820 Some(language)
13821 }
13822 })
13823 .cloned()
13824 .collect::<HashSet<_>>();
13825 if !languages_affected.is_empty() {
13826 self.refresh_inlay_hints(
13827 InlayHintRefreshReason::BufferEdited(languages_affected),
13828 cx,
13829 );
13830 }
13831 }
13832 }
13833
13834 let Some(project) = &self.project else { return };
13835 let (telemetry, is_via_ssh) = {
13836 let project = project.read(cx);
13837 let telemetry = project.client().telemetry().clone();
13838 let is_via_ssh = project.is_via_ssh();
13839 (telemetry, is_via_ssh)
13840 };
13841 refresh_linked_ranges(self, window, cx);
13842 telemetry.log_edit_event("editor", is_via_ssh);
13843 }
13844 multi_buffer::Event::ExcerptsAdded {
13845 buffer,
13846 predecessor,
13847 excerpts,
13848 } => {
13849 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13850 let buffer_id = buffer.read(cx).remote_id();
13851 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
13852 if let Some(project) = &self.project {
13853 get_uncommitted_diff_for_buffer(
13854 project,
13855 [buffer.clone()],
13856 self.buffer.clone(),
13857 cx,
13858 );
13859 }
13860 }
13861 cx.emit(EditorEvent::ExcerptsAdded {
13862 buffer: buffer.clone(),
13863 predecessor: *predecessor,
13864 excerpts: excerpts.clone(),
13865 });
13866 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
13867 }
13868 multi_buffer::Event::ExcerptsRemoved { ids } => {
13869 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
13870 let buffer = self.buffer.read(cx);
13871 self.registered_buffers
13872 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
13873 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
13874 }
13875 multi_buffer::Event::ExcerptsEdited { ids } => {
13876 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
13877 }
13878 multi_buffer::Event::ExcerptsExpanded { ids } => {
13879 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
13880 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
13881 }
13882 multi_buffer::Event::Reparsed(buffer_id) => {
13883 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13884
13885 cx.emit(EditorEvent::Reparsed(*buffer_id));
13886 }
13887 multi_buffer::Event::DiffHunksToggled => {
13888 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13889 }
13890 multi_buffer::Event::LanguageChanged(buffer_id) => {
13891 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
13892 cx.emit(EditorEvent::Reparsed(*buffer_id));
13893 cx.notify();
13894 }
13895 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
13896 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
13897 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
13898 cx.emit(EditorEvent::TitleChanged)
13899 }
13900 // multi_buffer::Event::DiffBaseChanged => {
13901 // self.scrollbar_marker_state.dirty = true;
13902 // cx.emit(EditorEvent::DiffBaseChanged);
13903 // cx.notify();
13904 // }
13905 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
13906 multi_buffer::Event::DiagnosticsUpdated => {
13907 self.refresh_active_diagnostics(cx);
13908 self.scrollbar_marker_state.dirty = true;
13909 cx.notify();
13910 }
13911 _ => {}
13912 };
13913 }
13914
13915 fn on_display_map_changed(
13916 &mut self,
13917 _: Entity<DisplayMap>,
13918 _: &mut Window,
13919 cx: &mut Context<Self>,
13920 ) {
13921 cx.notify();
13922 }
13923
13924 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
13925 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13926 self.refresh_inline_completion(true, false, window, cx);
13927 self.refresh_inlay_hints(
13928 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
13929 self.selections.newest_anchor().head(),
13930 &self.buffer.read(cx).snapshot(cx),
13931 cx,
13932 )),
13933 cx,
13934 );
13935
13936 let old_cursor_shape = self.cursor_shape;
13937
13938 {
13939 let editor_settings = EditorSettings::get_global(cx);
13940 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
13941 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
13942 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
13943 }
13944
13945 if old_cursor_shape != self.cursor_shape {
13946 cx.emit(EditorEvent::CursorShapeChanged);
13947 }
13948
13949 let project_settings = ProjectSettings::get_global(cx);
13950 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
13951
13952 if self.mode == EditorMode::Full {
13953 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
13954 if self.git_blame_inline_enabled != inline_blame_enabled {
13955 self.toggle_git_blame_inline_internal(false, window, cx);
13956 }
13957 }
13958
13959 cx.notify();
13960 }
13961
13962 pub fn set_searchable(&mut self, searchable: bool) {
13963 self.searchable = searchable;
13964 }
13965
13966 pub fn searchable(&self) -> bool {
13967 self.searchable
13968 }
13969
13970 fn open_proposed_changes_editor(
13971 &mut self,
13972 _: &OpenProposedChangesEditor,
13973 window: &mut Window,
13974 cx: &mut Context<Self>,
13975 ) {
13976 let Some(workspace) = self.workspace() else {
13977 cx.propagate();
13978 return;
13979 };
13980
13981 let selections = self.selections.all::<usize>(cx);
13982 let multi_buffer = self.buffer.read(cx);
13983 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13984 let mut new_selections_by_buffer = HashMap::default();
13985 for selection in selections {
13986 for (buffer, range, _) in
13987 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
13988 {
13989 let mut range = range.to_point(buffer);
13990 range.start.column = 0;
13991 range.end.column = buffer.line_len(range.end.row);
13992 new_selections_by_buffer
13993 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
13994 .or_insert(Vec::new())
13995 .push(range)
13996 }
13997 }
13998
13999 let proposed_changes_buffers = new_selections_by_buffer
14000 .into_iter()
14001 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
14002 .collect::<Vec<_>>();
14003 let proposed_changes_editor = cx.new(|cx| {
14004 ProposedChangesEditor::new(
14005 "Proposed changes",
14006 proposed_changes_buffers,
14007 self.project.clone(),
14008 window,
14009 cx,
14010 )
14011 });
14012
14013 window.defer(cx, move |window, cx| {
14014 workspace.update(cx, |workspace, cx| {
14015 workspace.active_pane().update(cx, |pane, cx| {
14016 pane.add_item(
14017 Box::new(proposed_changes_editor),
14018 true,
14019 true,
14020 None,
14021 window,
14022 cx,
14023 );
14024 });
14025 });
14026 });
14027 }
14028
14029 pub fn open_excerpts_in_split(
14030 &mut self,
14031 _: &OpenExcerptsSplit,
14032 window: &mut Window,
14033 cx: &mut Context<Self>,
14034 ) {
14035 self.open_excerpts_common(None, true, window, cx)
14036 }
14037
14038 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
14039 self.open_excerpts_common(None, false, window, cx)
14040 }
14041
14042 fn open_excerpts_common(
14043 &mut self,
14044 jump_data: Option<JumpData>,
14045 split: bool,
14046 window: &mut Window,
14047 cx: &mut Context<Self>,
14048 ) {
14049 let Some(workspace) = self.workspace() else {
14050 cx.propagate();
14051 return;
14052 };
14053
14054 if self.buffer.read(cx).is_singleton() {
14055 cx.propagate();
14056 return;
14057 }
14058
14059 let mut new_selections_by_buffer = HashMap::default();
14060 match &jump_data {
14061 Some(JumpData::MultiBufferPoint {
14062 excerpt_id,
14063 position,
14064 anchor,
14065 line_offset_from_top,
14066 }) => {
14067 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
14068 if let Some(buffer) = multi_buffer_snapshot
14069 .buffer_id_for_excerpt(*excerpt_id)
14070 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
14071 {
14072 let buffer_snapshot = buffer.read(cx).snapshot();
14073 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
14074 language::ToPoint::to_point(anchor, &buffer_snapshot)
14075 } else {
14076 buffer_snapshot.clip_point(*position, Bias::Left)
14077 };
14078 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
14079 new_selections_by_buffer.insert(
14080 buffer,
14081 (
14082 vec![jump_to_offset..jump_to_offset],
14083 Some(*line_offset_from_top),
14084 ),
14085 );
14086 }
14087 }
14088 Some(JumpData::MultiBufferRow {
14089 row,
14090 line_offset_from_top,
14091 }) => {
14092 let point = MultiBufferPoint::new(row.0, 0);
14093 if let Some((buffer, buffer_point, _)) =
14094 self.buffer.read(cx).point_to_buffer_point(point, cx)
14095 {
14096 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
14097 new_selections_by_buffer
14098 .entry(buffer)
14099 .or_insert((Vec::new(), Some(*line_offset_from_top)))
14100 .0
14101 .push(buffer_offset..buffer_offset)
14102 }
14103 }
14104 None => {
14105 let selections = self.selections.all::<usize>(cx);
14106 let multi_buffer = self.buffer.read(cx);
14107 for selection in selections {
14108 for (buffer, mut range, _) in multi_buffer
14109 .snapshot(cx)
14110 .range_to_buffer_ranges(selection.range())
14111 {
14112 // When editing branch buffers, jump to the corresponding location
14113 // in their base buffer.
14114 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
14115 let buffer = buffer_handle.read(cx);
14116 if let Some(base_buffer) = buffer.base_buffer() {
14117 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
14118 buffer_handle = base_buffer;
14119 }
14120
14121 if selection.reversed {
14122 mem::swap(&mut range.start, &mut range.end);
14123 }
14124 new_selections_by_buffer
14125 .entry(buffer_handle)
14126 .or_insert((Vec::new(), None))
14127 .0
14128 .push(range)
14129 }
14130 }
14131 }
14132 }
14133
14134 if new_selections_by_buffer.is_empty() {
14135 return;
14136 }
14137
14138 // We defer the pane interaction because we ourselves are a workspace item
14139 // and activating a new item causes the pane to call a method on us reentrantly,
14140 // which panics if we're on the stack.
14141 window.defer(cx, move |window, cx| {
14142 workspace.update(cx, |workspace, cx| {
14143 let pane = if split {
14144 workspace.adjacent_pane(window, cx)
14145 } else {
14146 workspace.active_pane().clone()
14147 };
14148
14149 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
14150 let editor = buffer
14151 .read(cx)
14152 .file()
14153 .is_none()
14154 .then(|| {
14155 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
14156 // so `workspace.open_project_item` will never find them, always opening a new editor.
14157 // Instead, we try to activate the existing editor in the pane first.
14158 let (editor, pane_item_index) =
14159 pane.read(cx).items().enumerate().find_map(|(i, item)| {
14160 let editor = item.downcast::<Editor>()?;
14161 let singleton_buffer =
14162 editor.read(cx).buffer().read(cx).as_singleton()?;
14163 if singleton_buffer == buffer {
14164 Some((editor, i))
14165 } else {
14166 None
14167 }
14168 })?;
14169 pane.update(cx, |pane, cx| {
14170 pane.activate_item(pane_item_index, true, true, window, cx)
14171 });
14172 Some(editor)
14173 })
14174 .flatten()
14175 .unwrap_or_else(|| {
14176 workspace.open_project_item::<Self>(
14177 pane.clone(),
14178 buffer,
14179 true,
14180 true,
14181 window,
14182 cx,
14183 )
14184 });
14185
14186 editor.update(cx, |editor, cx| {
14187 let autoscroll = match scroll_offset {
14188 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
14189 None => Autoscroll::newest(),
14190 };
14191 let nav_history = editor.nav_history.take();
14192 editor.change_selections(Some(autoscroll), window, cx, |s| {
14193 s.select_ranges(ranges);
14194 });
14195 editor.nav_history = nav_history;
14196 });
14197 }
14198 })
14199 });
14200 }
14201
14202 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
14203 let snapshot = self.buffer.read(cx).read(cx);
14204 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
14205 Some(
14206 ranges
14207 .iter()
14208 .map(move |range| {
14209 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
14210 })
14211 .collect(),
14212 )
14213 }
14214
14215 fn selection_replacement_ranges(
14216 &self,
14217 range: Range<OffsetUtf16>,
14218 cx: &mut App,
14219 ) -> Vec<Range<OffsetUtf16>> {
14220 let selections = self.selections.all::<OffsetUtf16>(cx);
14221 let newest_selection = selections
14222 .iter()
14223 .max_by_key(|selection| selection.id)
14224 .unwrap();
14225 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
14226 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
14227 let snapshot = self.buffer.read(cx).read(cx);
14228 selections
14229 .into_iter()
14230 .map(|mut selection| {
14231 selection.start.0 =
14232 (selection.start.0 as isize).saturating_add(start_delta) as usize;
14233 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
14234 snapshot.clip_offset_utf16(selection.start, Bias::Left)
14235 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
14236 })
14237 .collect()
14238 }
14239
14240 fn report_editor_event(
14241 &self,
14242 event_type: &'static str,
14243 file_extension: Option<String>,
14244 cx: &App,
14245 ) {
14246 if cfg!(any(test, feature = "test-support")) {
14247 return;
14248 }
14249
14250 let Some(project) = &self.project else { return };
14251
14252 // If None, we are in a file without an extension
14253 let file = self
14254 .buffer
14255 .read(cx)
14256 .as_singleton()
14257 .and_then(|b| b.read(cx).file());
14258 let file_extension = file_extension.or(file
14259 .as_ref()
14260 .and_then(|file| Path::new(file.file_name(cx)).extension())
14261 .and_then(|e| e.to_str())
14262 .map(|a| a.to_string()));
14263
14264 let vim_mode = cx
14265 .global::<SettingsStore>()
14266 .raw_user_settings()
14267 .get("vim_mode")
14268 == Some(&serde_json::Value::Bool(true));
14269
14270 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
14271 let copilot_enabled = edit_predictions_provider
14272 == language::language_settings::EditPredictionProvider::Copilot;
14273 let copilot_enabled_for_language = self
14274 .buffer
14275 .read(cx)
14276 .settings_at(0, cx)
14277 .show_edit_predictions;
14278
14279 let project = project.read(cx);
14280 telemetry::event!(
14281 event_type,
14282 file_extension,
14283 vim_mode,
14284 copilot_enabled,
14285 copilot_enabled_for_language,
14286 edit_predictions_provider,
14287 is_via_ssh = project.is_via_ssh(),
14288 );
14289 }
14290
14291 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
14292 /// with each line being an array of {text, highlight} objects.
14293 fn copy_highlight_json(
14294 &mut self,
14295 _: &CopyHighlightJson,
14296 window: &mut Window,
14297 cx: &mut Context<Self>,
14298 ) {
14299 #[derive(Serialize)]
14300 struct Chunk<'a> {
14301 text: String,
14302 highlight: Option<&'a str>,
14303 }
14304
14305 let snapshot = self.buffer.read(cx).snapshot(cx);
14306 let range = self
14307 .selected_text_range(false, window, cx)
14308 .and_then(|selection| {
14309 if selection.range.is_empty() {
14310 None
14311 } else {
14312 Some(selection.range)
14313 }
14314 })
14315 .unwrap_or_else(|| 0..snapshot.len());
14316
14317 let chunks = snapshot.chunks(range, true);
14318 let mut lines = Vec::new();
14319 let mut line: VecDeque<Chunk> = VecDeque::new();
14320
14321 let Some(style) = self.style.as_ref() else {
14322 return;
14323 };
14324
14325 for chunk in chunks {
14326 let highlight = chunk
14327 .syntax_highlight_id
14328 .and_then(|id| id.name(&style.syntax));
14329 let mut chunk_lines = chunk.text.split('\n').peekable();
14330 while let Some(text) = chunk_lines.next() {
14331 let mut merged_with_last_token = false;
14332 if let Some(last_token) = line.back_mut() {
14333 if last_token.highlight == highlight {
14334 last_token.text.push_str(text);
14335 merged_with_last_token = true;
14336 }
14337 }
14338
14339 if !merged_with_last_token {
14340 line.push_back(Chunk {
14341 text: text.into(),
14342 highlight,
14343 });
14344 }
14345
14346 if chunk_lines.peek().is_some() {
14347 if line.len() > 1 && line.front().unwrap().text.is_empty() {
14348 line.pop_front();
14349 }
14350 if line.len() > 1 && line.back().unwrap().text.is_empty() {
14351 line.pop_back();
14352 }
14353
14354 lines.push(mem::take(&mut line));
14355 }
14356 }
14357 }
14358
14359 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
14360 return;
14361 };
14362 cx.write_to_clipboard(ClipboardItem::new_string(lines));
14363 }
14364
14365 pub fn open_context_menu(
14366 &mut self,
14367 _: &OpenContextMenu,
14368 window: &mut Window,
14369 cx: &mut Context<Self>,
14370 ) {
14371 self.request_autoscroll(Autoscroll::newest(), cx);
14372 let position = self.selections.newest_display(cx).start;
14373 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
14374 }
14375
14376 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
14377 &self.inlay_hint_cache
14378 }
14379
14380 pub fn replay_insert_event(
14381 &mut self,
14382 text: &str,
14383 relative_utf16_range: Option<Range<isize>>,
14384 window: &mut Window,
14385 cx: &mut Context<Self>,
14386 ) {
14387 if !self.input_enabled {
14388 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14389 return;
14390 }
14391 if let Some(relative_utf16_range) = relative_utf16_range {
14392 let selections = self.selections.all::<OffsetUtf16>(cx);
14393 self.change_selections(None, window, cx, |s| {
14394 let new_ranges = selections.into_iter().map(|range| {
14395 let start = OffsetUtf16(
14396 range
14397 .head()
14398 .0
14399 .saturating_add_signed(relative_utf16_range.start),
14400 );
14401 let end = OffsetUtf16(
14402 range
14403 .head()
14404 .0
14405 .saturating_add_signed(relative_utf16_range.end),
14406 );
14407 start..end
14408 });
14409 s.select_ranges(new_ranges);
14410 });
14411 }
14412
14413 self.handle_input(text, window, cx);
14414 }
14415
14416 pub fn supports_inlay_hints(&self, cx: &App) -> bool {
14417 let Some(provider) = self.semantics_provider.as_ref() else {
14418 return false;
14419 };
14420
14421 let mut supports = false;
14422 self.buffer().read(cx).for_each_buffer(|buffer| {
14423 supports |= provider.supports_inlay_hints(buffer, cx);
14424 });
14425 supports
14426 }
14427
14428 pub fn is_focused(&self, window: &Window) -> bool {
14429 self.focus_handle.is_focused(window)
14430 }
14431
14432 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14433 cx.emit(EditorEvent::Focused);
14434
14435 if let Some(descendant) = self
14436 .last_focused_descendant
14437 .take()
14438 .and_then(|descendant| descendant.upgrade())
14439 {
14440 window.focus(&descendant);
14441 } else {
14442 if let Some(blame) = self.blame.as_ref() {
14443 blame.update(cx, GitBlame::focus)
14444 }
14445
14446 self.blink_manager.update(cx, BlinkManager::enable);
14447 self.show_cursor_names(window, cx);
14448 self.buffer.update(cx, |buffer, cx| {
14449 buffer.finalize_last_transaction(cx);
14450 if self.leader_peer_id.is_none() {
14451 buffer.set_active_selections(
14452 &self.selections.disjoint_anchors(),
14453 self.selections.line_mode,
14454 self.cursor_shape,
14455 cx,
14456 );
14457 }
14458 });
14459 }
14460 }
14461
14462 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
14463 cx.emit(EditorEvent::FocusedIn)
14464 }
14465
14466 fn handle_focus_out(
14467 &mut self,
14468 event: FocusOutEvent,
14469 _window: &mut Window,
14470 _cx: &mut Context<Self>,
14471 ) {
14472 if event.blurred != self.focus_handle {
14473 self.last_focused_descendant = Some(event.blurred);
14474 }
14475 }
14476
14477 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14478 self.blink_manager.update(cx, BlinkManager::disable);
14479 self.buffer
14480 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
14481
14482 if let Some(blame) = self.blame.as_ref() {
14483 blame.update(cx, GitBlame::blur)
14484 }
14485 if !self.hover_state.focused(window, cx) {
14486 hide_hover(self, cx);
14487 }
14488
14489 self.hide_context_menu(window, cx);
14490 cx.emit(EditorEvent::Blurred);
14491 cx.notify();
14492 }
14493
14494 pub fn register_action<A: Action>(
14495 &mut self,
14496 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
14497 ) -> Subscription {
14498 let id = self.next_editor_action_id.post_inc();
14499 let listener = Arc::new(listener);
14500 self.editor_actions.borrow_mut().insert(
14501 id,
14502 Box::new(move |window, _| {
14503 let listener = listener.clone();
14504 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
14505 let action = action.downcast_ref().unwrap();
14506 if phase == DispatchPhase::Bubble {
14507 listener(action, window, cx)
14508 }
14509 })
14510 }),
14511 );
14512
14513 let editor_actions = self.editor_actions.clone();
14514 Subscription::new(move || {
14515 editor_actions.borrow_mut().remove(&id);
14516 })
14517 }
14518
14519 pub fn file_header_size(&self) -> u32 {
14520 FILE_HEADER_HEIGHT
14521 }
14522
14523 pub fn revert(
14524 &mut self,
14525 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
14526 window: &mut Window,
14527 cx: &mut Context<Self>,
14528 ) {
14529 self.buffer().update(cx, |multi_buffer, cx| {
14530 for (buffer_id, changes) in revert_changes {
14531 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
14532 buffer.update(cx, |buffer, cx| {
14533 buffer.edit(
14534 changes.into_iter().map(|(range, text)| {
14535 (range, text.to_string().map(Arc::<str>::from))
14536 }),
14537 None,
14538 cx,
14539 );
14540 });
14541 }
14542 }
14543 });
14544 self.change_selections(None, window, cx, |selections| selections.refresh());
14545 }
14546
14547 pub fn to_pixel_point(
14548 &self,
14549 source: multi_buffer::Anchor,
14550 editor_snapshot: &EditorSnapshot,
14551 window: &mut Window,
14552 ) -> Option<gpui::Point<Pixels>> {
14553 let source_point = source.to_display_point(editor_snapshot);
14554 self.display_to_pixel_point(source_point, editor_snapshot, window)
14555 }
14556
14557 pub fn display_to_pixel_point(
14558 &self,
14559 source: DisplayPoint,
14560 editor_snapshot: &EditorSnapshot,
14561 window: &mut Window,
14562 ) -> Option<gpui::Point<Pixels>> {
14563 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
14564 let text_layout_details = self.text_layout_details(window);
14565 let scroll_top = text_layout_details
14566 .scroll_anchor
14567 .scroll_position(editor_snapshot)
14568 .y;
14569
14570 if source.row().as_f32() < scroll_top.floor() {
14571 return None;
14572 }
14573 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
14574 let source_y = line_height * (source.row().as_f32() - scroll_top);
14575 Some(gpui::Point::new(source_x, source_y))
14576 }
14577
14578 pub fn has_visible_completions_menu(&self) -> bool {
14579 !self.previewing_inline_completion
14580 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
14581 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
14582 })
14583 }
14584
14585 pub fn register_addon<T: Addon>(&mut self, instance: T) {
14586 self.addons
14587 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
14588 }
14589
14590 pub fn unregister_addon<T: Addon>(&mut self) {
14591 self.addons.remove(&std::any::TypeId::of::<T>());
14592 }
14593
14594 pub fn addon<T: Addon>(&self) -> Option<&T> {
14595 let type_id = std::any::TypeId::of::<T>();
14596 self.addons
14597 .get(&type_id)
14598 .and_then(|item| item.to_any().downcast_ref::<T>())
14599 }
14600
14601 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
14602 let text_layout_details = self.text_layout_details(window);
14603 let style = &text_layout_details.editor_style;
14604 let font_id = window.text_system().resolve_font(&style.text.font());
14605 let font_size = style.text.font_size.to_pixels(window.rem_size());
14606 let line_height = style.text.line_height_in_pixels(window.rem_size());
14607 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
14608
14609 gpui::Size::new(em_width, line_height)
14610 }
14611}
14612
14613fn get_uncommitted_diff_for_buffer(
14614 project: &Entity<Project>,
14615 buffers: impl IntoIterator<Item = Entity<Buffer>>,
14616 buffer: Entity<MultiBuffer>,
14617 cx: &mut App,
14618) {
14619 let mut tasks = Vec::new();
14620 project.update(cx, |project, cx| {
14621 for buffer in buffers {
14622 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
14623 }
14624 });
14625 cx.spawn(|mut cx| async move {
14626 let diffs = futures::future::join_all(tasks).await;
14627 buffer
14628 .update(&mut cx, |buffer, cx| {
14629 for diff in diffs.into_iter().flatten() {
14630 buffer.add_diff(diff, cx);
14631 }
14632 })
14633 .ok();
14634 })
14635 .detach();
14636}
14637
14638fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
14639 let tab_size = tab_size.get() as usize;
14640 let mut width = offset;
14641
14642 for ch in text.chars() {
14643 width += if ch == '\t' {
14644 tab_size - (width % tab_size)
14645 } else {
14646 1
14647 };
14648 }
14649
14650 width - offset
14651}
14652
14653#[cfg(test)]
14654mod tests {
14655 use super::*;
14656
14657 #[test]
14658 fn test_string_size_with_expanded_tabs() {
14659 let nz = |val| NonZeroU32::new(val).unwrap();
14660 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
14661 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
14662 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
14663 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
14664 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
14665 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
14666 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
14667 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
14668 }
14669}
14670
14671/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
14672struct WordBreakingTokenizer<'a> {
14673 input: &'a str,
14674}
14675
14676impl<'a> WordBreakingTokenizer<'a> {
14677 fn new(input: &'a str) -> Self {
14678 Self { input }
14679 }
14680}
14681
14682fn is_char_ideographic(ch: char) -> bool {
14683 use unicode_script::Script::*;
14684 use unicode_script::UnicodeScript;
14685 matches!(ch.script(), Han | Tangut | Yi)
14686}
14687
14688fn is_grapheme_ideographic(text: &str) -> bool {
14689 text.chars().any(is_char_ideographic)
14690}
14691
14692fn is_grapheme_whitespace(text: &str) -> bool {
14693 text.chars().any(|x| x.is_whitespace())
14694}
14695
14696fn should_stay_with_preceding_ideograph(text: &str) -> bool {
14697 text.chars().next().map_or(false, |ch| {
14698 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
14699 })
14700}
14701
14702#[derive(PartialEq, Eq, Debug, Clone, Copy)]
14703struct WordBreakToken<'a> {
14704 token: &'a str,
14705 grapheme_len: usize,
14706 is_whitespace: bool,
14707}
14708
14709impl<'a> Iterator for WordBreakingTokenizer<'a> {
14710 /// Yields a span, the count of graphemes in the token, and whether it was
14711 /// whitespace. Note that it also breaks at word boundaries.
14712 type Item = WordBreakToken<'a>;
14713
14714 fn next(&mut self) -> Option<Self::Item> {
14715 use unicode_segmentation::UnicodeSegmentation;
14716 if self.input.is_empty() {
14717 return None;
14718 }
14719
14720 let mut iter = self.input.graphemes(true).peekable();
14721 let mut offset = 0;
14722 let mut graphemes = 0;
14723 if let Some(first_grapheme) = iter.next() {
14724 let is_whitespace = is_grapheme_whitespace(first_grapheme);
14725 offset += first_grapheme.len();
14726 graphemes += 1;
14727 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
14728 if let Some(grapheme) = iter.peek().copied() {
14729 if should_stay_with_preceding_ideograph(grapheme) {
14730 offset += grapheme.len();
14731 graphemes += 1;
14732 }
14733 }
14734 } else {
14735 let mut words = self.input[offset..].split_word_bound_indices().peekable();
14736 let mut next_word_bound = words.peek().copied();
14737 if next_word_bound.map_or(false, |(i, _)| i == 0) {
14738 next_word_bound = words.next();
14739 }
14740 while let Some(grapheme) = iter.peek().copied() {
14741 if next_word_bound.map_or(false, |(i, _)| i == offset) {
14742 break;
14743 };
14744 if is_grapheme_whitespace(grapheme) != is_whitespace {
14745 break;
14746 };
14747 offset += grapheme.len();
14748 graphemes += 1;
14749 iter.next();
14750 }
14751 }
14752 let token = &self.input[..offset];
14753 self.input = &self.input[offset..];
14754 if is_whitespace {
14755 Some(WordBreakToken {
14756 token: " ",
14757 grapheme_len: 1,
14758 is_whitespace: true,
14759 })
14760 } else {
14761 Some(WordBreakToken {
14762 token,
14763 grapheme_len: graphemes,
14764 is_whitespace: false,
14765 })
14766 }
14767 } else {
14768 None
14769 }
14770 }
14771}
14772
14773#[test]
14774fn test_word_breaking_tokenizer() {
14775 let tests: &[(&str, &[(&str, usize, bool)])] = &[
14776 ("", &[]),
14777 (" ", &[(" ", 1, true)]),
14778 ("Ʒ", &[("Ʒ", 1, false)]),
14779 ("Ǽ", &[("Ǽ", 1, false)]),
14780 ("⋑", &[("⋑", 1, false)]),
14781 ("⋑⋑", &[("⋑⋑", 2, false)]),
14782 (
14783 "原理,进而",
14784 &[
14785 ("原", 1, false),
14786 ("理,", 2, false),
14787 ("进", 1, false),
14788 ("而", 1, false),
14789 ],
14790 ),
14791 (
14792 "hello world",
14793 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
14794 ),
14795 (
14796 "hello, world",
14797 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
14798 ),
14799 (
14800 " hello world",
14801 &[
14802 (" ", 1, true),
14803 ("hello", 5, false),
14804 (" ", 1, true),
14805 ("world", 5, false),
14806 ],
14807 ),
14808 (
14809 "这是什么 \n 钢笔",
14810 &[
14811 ("这", 1, false),
14812 ("是", 1, false),
14813 ("什", 1, false),
14814 ("么", 1, false),
14815 (" ", 1, true),
14816 ("钢", 1, false),
14817 ("笔", 1, false),
14818 ],
14819 ),
14820 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
14821 ];
14822
14823 for (input, result) in tests {
14824 assert_eq!(
14825 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
14826 result
14827 .iter()
14828 .copied()
14829 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
14830 token,
14831 grapheme_len,
14832 is_whitespace,
14833 })
14834 .collect::<Vec<_>>()
14835 );
14836 }
14837}
14838
14839fn wrap_with_prefix(
14840 line_prefix: String,
14841 unwrapped_text: String,
14842 wrap_column: usize,
14843 tab_size: NonZeroU32,
14844) -> String {
14845 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
14846 let mut wrapped_text = String::new();
14847 let mut current_line = line_prefix.clone();
14848
14849 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
14850 let mut current_line_len = line_prefix_len;
14851 for WordBreakToken {
14852 token,
14853 grapheme_len,
14854 is_whitespace,
14855 } in tokenizer
14856 {
14857 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
14858 wrapped_text.push_str(current_line.trim_end());
14859 wrapped_text.push('\n');
14860 current_line.truncate(line_prefix.len());
14861 current_line_len = line_prefix_len;
14862 if !is_whitespace {
14863 current_line.push_str(token);
14864 current_line_len += grapheme_len;
14865 }
14866 } else if !is_whitespace {
14867 current_line.push_str(token);
14868 current_line_len += grapheme_len;
14869 } else if current_line_len != line_prefix_len {
14870 current_line.push(' ');
14871 current_line_len += 1;
14872 }
14873 }
14874
14875 if !current_line.is_empty() {
14876 wrapped_text.push_str(¤t_line);
14877 }
14878 wrapped_text
14879}
14880
14881#[test]
14882fn test_wrap_with_prefix() {
14883 assert_eq!(
14884 wrap_with_prefix(
14885 "# ".to_string(),
14886 "abcdefg".to_string(),
14887 4,
14888 NonZeroU32::new(4).unwrap()
14889 ),
14890 "# abcdefg"
14891 );
14892 assert_eq!(
14893 wrap_with_prefix(
14894 "".to_string(),
14895 "\thello world".to_string(),
14896 8,
14897 NonZeroU32::new(4).unwrap()
14898 ),
14899 "hello\nworld"
14900 );
14901 assert_eq!(
14902 wrap_with_prefix(
14903 "// ".to_string(),
14904 "xx \nyy zz aa bb cc".to_string(),
14905 12,
14906 NonZeroU32::new(4).unwrap()
14907 ),
14908 "// xx yy zz\n// aa bb cc"
14909 );
14910 assert_eq!(
14911 wrap_with_prefix(
14912 String::new(),
14913 "这是什么 \n 钢笔".to_string(),
14914 3,
14915 NonZeroU32::new(4).unwrap()
14916 ),
14917 "这是什\n么 钢\n笔"
14918 );
14919}
14920
14921pub trait CollaborationHub {
14922 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
14923 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
14924 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
14925}
14926
14927impl CollaborationHub for Entity<Project> {
14928 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
14929 self.read(cx).collaborators()
14930 }
14931
14932 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
14933 self.read(cx).user_store().read(cx).participant_indices()
14934 }
14935
14936 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
14937 let this = self.read(cx);
14938 let user_ids = this.collaborators().values().map(|c| c.user_id);
14939 this.user_store().read_with(cx, |user_store, cx| {
14940 user_store.participant_names(user_ids, cx)
14941 })
14942 }
14943}
14944
14945pub trait SemanticsProvider {
14946 fn hover(
14947 &self,
14948 buffer: &Entity<Buffer>,
14949 position: text::Anchor,
14950 cx: &mut App,
14951 ) -> Option<Task<Vec<project::Hover>>>;
14952
14953 fn inlay_hints(
14954 &self,
14955 buffer_handle: Entity<Buffer>,
14956 range: Range<text::Anchor>,
14957 cx: &mut App,
14958 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
14959
14960 fn resolve_inlay_hint(
14961 &self,
14962 hint: InlayHint,
14963 buffer_handle: Entity<Buffer>,
14964 server_id: LanguageServerId,
14965 cx: &mut App,
14966 ) -> Option<Task<anyhow::Result<InlayHint>>>;
14967
14968 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool;
14969
14970 fn document_highlights(
14971 &self,
14972 buffer: &Entity<Buffer>,
14973 position: text::Anchor,
14974 cx: &mut App,
14975 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
14976
14977 fn definitions(
14978 &self,
14979 buffer: &Entity<Buffer>,
14980 position: text::Anchor,
14981 kind: GotoDefinitionKind,
14982 cx: &mut App,
14983 ) -> Option<Task<Result<Vec<LocationLink>>>>;
14984
14985 fn range_for_rename(
14986 &self,
14987 buffer: &Entity<Buffer>,
14988 position: text::Anchor,
14989 cx: &mut App,
14990 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
14991
14992 fn perform_rename(
14993 &self,
14994 buffer: &Entity<Buffer>,
14995 position: text::Anchor,
14996 new_name: String,
14997 cx: &mut App,
14998 ) -> Option<Task<Result<ProjectTransaction>>>;
14999}
15000
15001pub trait CompletionProvider {
15002 fn completions(
15003 &self,
15004 buffer: &Entity<Buffer>,
15005 buffer_position: text::Anchor,
15006 trigger: CompletionContext,
15007 window: &mut Window,
15008 cx: &mut Context<Editor>,
15009 ) -> Task<Result<Vec<Completion>>>;
15010
15011 fn resolve_completions(
15012 &self,
15013 buffer: Entity<Buffer>,
15014 completion_indices: Vec<usize>,
15015 completions: Rc<RefCell<Box<[Completion]>>>,
15016 cx: &mut Context<Editor>,
15017 ) -> Task<Result<bool>>;
15018
15019 fn apply_additional_edits_for_completion(
15020 &self,
15021 _buffer: Entity<Buffer>,
15022 _completions: Rc<RefCell<Box<[Completion]>>>,
15023 _completion_index: usize,
15024 _push_to_history: bool,
15025 _cx: &mut Context<Editor>,
15026 ) -> Task<Result<Option<language::Transaction>>> {
15027 Task::ready(Ok(None))
15028 }
15029
15030 fn is_completion_trigger(
15031 &self,
15032 buffer: &Entity<Buffer>,
15033 position: language::Anchor,
15034 text: &str,
15035 trigger_in_words: bool,
15036 cx: &mut Context<Editor>,
15037 ) -> bool;
15038
15039 fn sort_completions(&self) -> bool {
15040 true
15041 }
15042}
15043
15044pub trait CodeActionProvider {
15045 fn id(&self) -> Arc<str>;
15046
15047 fn code_actions(
15048 &self,
15049 buffer: &Entity<Buffer>,
15050 range: Range<text::Anchor>,
15051 window: &mut Window,
15052 cx: &mut App,
15053 ) -> Task<Result<Vec<CodeAction>>>;
15054
15055 fn apply_code_action(
15056 &self,
15057 buffer_handle: Entity<Buffer>,
15058 action: CodeAction,
15059 excerpt_id: ExcerptId,
15060 push_to_history: bool,
15061 window: &mut Window,
15062 cx: &mut App,
15063 ) -> Task<Result<ProjectTransaction>>;
15064}
15065
15066impl CodeActionProvider for Entity<Project> {
15067 fn id(&self) -> Arc<str> {
15068 "project".into()
15069 }
15070
15071 fn code_actions(
15072 &self,
15073 buffer: &Entity<Buffer>,
15074 range: Range<text::Anchor>,
15075 _window: &mut Window,
15076 cx: &mut App,
15077 ) -> Task<Result<Vec<CodeAction>>> {
15078 self.update(cx, |project, cx| {
15079 project.code_actions(buffer, range, None, cx)
15080 })
15081 }
15082
15083 fn apply_code_action(
15084 &self,
15085 buffer_handle: Entity<Buffer>,
15086 action: CodeAction,
15087 _excerpt_id: ExcerptId,
15088 push_to_history: bool,
15089 _window: &mut Window,
15090 cx: &mut App,
15091 ) -> Task<Result<ProjectTransaction>> {
15092 self.update(cx, |project, cx| {
15093 project.apply_code_action(buffer_handle, action, push_to_history, cx)
15094 })
15095 }
15096}
15097
15098fn snippet_completions(
15099 project: &Project,
15100 buffer: &Entity<Buffer>,
15101 buffer_position: text::Anchor,
15102 cx: &mut App,
15103) -> Task<Result<Vec<Completion>>> {
15104 let language = buffer.read(cx).language_at(buffer_position);
15105 let language_name = language.as_ref().map(|language| language.lsp_id());
15106 let snippet_store = project.snippets().read(cx);
15107 let snippets = snippet_store.snippets_for(language_name, cx);
15108
15109 if snippets.is_empty() {
15110 return Task::ready(Ok(vec![]));
15111 }
15112 let snapshot = buffer.read(cx).text_snapshot();
15113 let chars: String = snapshot
15114 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
15115 .collect();
15116
15117 let scope = language.map(|language| language.default_scope());
15118 let executor = cx.background_executor().clone();
15119
15120 cx.background_executor().spawn(async move {
15121 let classifier = CharClassifier::new(scope).for_completion(true);
15122 let mut last_word = chars
15123 .chars()
15124 .take_while(|c| classifier.is_word(*c))
15125 .collect::<String>();
15126 last_word = last_word.chars().rev().collect();
15127
15128 if last_word.is_empty() {
15129 return Ok(vec![]);
15130 }
15131
15132 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
15133 let to_lsp = |point: &text::Anchor| {
15134 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
15135 point_to_lsp(end)
15136 };
15137 let lsp_end = to_lsp(&buffer_position);
15138
15139 let candidates = snippets
15140 .iter()
15141 .enumerate()
15142 .flat_map(|(ix, snippet)| {
15143 snippet
15144 .prefix
15145 .iter()
15146 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
15147 })
15148 .collect::<Vec<StringMatchCandidate>>();
15149
15150 let mut matches = fuzzy::match_strings(
15151 &candidates,
15152 &last_word,
15153 last_word.chars().any(|c| c.is_uppercase()),
15154 100,
15155 &Default::default(),
15156 executor,
15157 )
15158 .await;
15159
15160 // Remove all candidates where the query's start does not match the start of any word in the candidate
15161 if let Some(query_start) = last_word.chars().next() {
15162 matches.retain(|string_match| {
15163 split_words(&string_match.string).any(|word| {
15164 // Check that the first codepoint of the word as lowercase matches the first
15165 // codepoint of the query as lowercase
15166 word.chars()
15167 .flat_map(|codepoint| codepoint.to_lowercase())
15168 .zip(query_start.to_lowercase())
15169 .all(|(word_cp, query_cp)| word_cp == query_cp)
15170 })
15171 });
15172 }
15173
15174 let matched_strings = matches
15175 .into_iter()
15176 .map(|m| m.string)
15177 .collect::<HashSet<_>>();
15178
15179 let result: Vec<Completion> = snippets
15180 .into_iter()
15181 .filter_map(|snippet| {
15182 let matching_prefix = snippet
15183 .prefix
15184 .iter()
15185 .find(|prefix| matched_strings.contains(*prefix))?;
15186 let start = as_offset - last_word.len();
15187 let start = snapshot.anchor_before(start);
15188 let range = start..buffer_position;
15189 let lsp_start = to_lsp(&start);
15190 let lsp_range = lsp::Range {
15191 start: lsp_start,
15192 end: lsp_end,
15193 };
15194 Some(Completion {
15195 old_range: range,
15196 new_text: snippet.body.clone(),
15197 resolved: false,
15198 label: CodeLabel {
15199 text: matching_prefix.clone(),
15200 runs: vec![],
15201 filter_range: 0..matching_prefix.len(),
15202 },
15203 server_id: LanguageServerId(usize::MAX),
15204 documentation: snippet
15205 .description
15206 .clone()
15207 .map(CompletionDocumentation::SingleLine),
15208 lsp_completion: lsp::CompletionItem {
15209 label: snippet.prefix.first().unwrap().clone(),
15210 kind: Some(CompletionItemKind::SNIPPET),
15211 label_details: snippet.description.as_ref().map(|description| {
15212 lsp::CompletionItemLabelDetails {
15213 detail: Some(description.clone()),
15214 description: None,
15215 }
15216 }),
15217 insert_text_format: Some(InsertTextFormat::SNIPPET),
15218 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
15219 lsp::InsertReplaceEdit {
15220 new_text: snippet.body.clone(),
15221 insert: lsp_range,
15222 replace: lsp_range,
15223 },
15224 )),
15225 filter_text: Some(snippet.body.clone()),
15226 sort_text: Some(char::MAX.to_string()),
15227 ..Default::default()
15228 },
15229 confirm: None,
15230 })
15231 })
15232 .collect();
15233
15234 Ok(result)
15235 })
15236}
15237
15238impl CompletionProvider for Entity<Project> {
15239 fn completions(
15240 &self,
15241 buffer: &Entity<Buffer>,
15242 buffer_position: text::Anchor,
15243 options: CompletionContext,
15244 _window: &mut Window,
15245 cx: &mut Context<Editor>,
15246 ) -> Task<Result<Vec<Completion>>> {
15247 self.update(cx, |project, cx| {
15248 let snippets = snippet_completions(project, buffer, buffer_position, cx);
15249 let project_completions = project.completions(buffer, buffer_position, options, cx);
15250 cx.background_executor().spawn(async move {
15251 let mut completions = project_completions.await?;
15252 let snippets_completions = snippets.await?;
15253 completions.extend(snippets_completions);
15254 Ok(completions)
15255 })
15256 })
15257 }
15258
15259 fn resolve_completions(
15260 &self,
15261 buffer: Entity<Buffer>,
15262 completion_indices: Vec<usize>,
15263 completions: Rc<RefCell<Box<[Completion]>>>,
15264 cx: &mut Context<Editor>,
15265 ) -> Task<Result<bool>> {
15266 self.update(cx, |project, cx| {
15267 project.lsp_store().update(cx, |lsp_store, cx| {
15268 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
15269 })
15270 })
15271 }
15272
15273 fn apply_additional_edits_for_completion(
15274 &self,
15275 buffer: Entity<Buffer>,
15276 completions: Rc<RefCell<Box<[Completion]>>>,
15277 completion_index: usize,
15278 push_to_history: bool,
15279 cx: &mut Context<Editor>,
15280 ) -> Task<Result<Option<language::Transaction>>> {
15281 self.update(cx, |project, cx| {
15282 project.lsp_store().update(cx, |lsp_store, cx| {
15283 lsp_store.apply_additional_edits_for_completion(
15284 buffer,
15285 completions,
15286 completion_index,
15287 push_to_history,
15288 cx,
15289 )
15290 })
15291 })
15292 }
15293
15294 fn is_completion_trigger(
15295 &self,
15296 buffer: &Entity<Buffer>,
15297 position: language::Anchor,
15298 text: &str,
15299 trigger_in_words: bool,
15300 cx: &mut Context<Editor>,
15301 ) -> bool {
15302 let mut chars = text.chars();
15303 let char = if let Some(char) = chars.next() {
15304 char
15305 } else {
15306 return false;
15307 };
15308 if chars.next().is_some() {
15309 return false;
15310 }
15311
15312 let buffer = buffer.read(cx);
15313 let snapshot = buffer.snapshot();
15314 if !snapshot.settings_at(position, cx).show_completions_on_input {
15315 return false;
15316 }
15317 let classifier = snapshot.char_classifier_at(position).for_completion(true);
15318 if trigger_in_words && classifier.is_word(char) {
15319 return true;
15320 }
15321
15322 buffer.completion_triggers().contains(text)
15323 }
15324}
15325
15326impl SemanticsProvider for Entity<Project> {
15327 fn hover(
15328 &self,
15329 buffer: &Entity<Buffer>,
15330 position: text::Anchor,
15331 cx: &mut App,
15332 ) -> Option<Task<Vec<project::Hover>>> {
15333 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
15334 }
15335
15336 fn document_highlights(
15337 &self,
15338 buffer: &Entity<Buffer>,
15339 position: text::Anchor,
15340 cx: &mut App,
15341 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
15342 Some(self.update(cx, |project, cx| {
15343 project.document_highlights(buffer, position, cx)
15344 }))
15345 }
15346
15347 fn definitions(
15348 &self,
15349 buffer: &Entity<Buffer>,
15350 position: text::Anchor,
15351 kind: GotoDefinitionKind,
15352 cx: &mut App,
15353 ) -> Option<Task<Result<Vec<LocationLink>>>> {
15354 Some(self.update(cx, |project, cx| match kind {
15355 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
15356 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
15357 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
15358 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
15359 }))
15360 }
15361
15362 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
15363 // TODO: make this work for remote projects
15364 self.read(cx)
15365 .language_servers_for_local_buffer(buffer.read(cx), cx)
15366 .any(
15367 |(_, server)| match server.capabilities().inlay_hint_provider {
15368 Some(lsp::OneOf::Left(enabled)) => enabled,
15369 Some(lsp::OneOf::Right(_)) => true,
15370 None => false,
15371 },
15372 )
15373 }
15374
15375 fn inlay_hints(
15376 &self,
15377 buffer_handle: Entity<Buffer>,
15378 range: Range<text::Anchor>,
15379 cx: &mut App,
15380 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
15381 Some(self.update(cx, |project, cx| {
15382 project.inlay_hints(buffer_handle, range, cx)
15383 }))
15384 }
15385
15386 fn resolve_inlay_hint(
15387 &self,
15388 hint: InlayHint,
15389 buffer_handle: Entity<Buffer>,
15390 server_id: LanguageServerId,
15391 cx: &mut App,
15392 ) -> Option<Task<anyhow::Result<InlayHint>>> {
15393 Some(self.update(cx, |project, cx| {
15394 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
15395 }))
15396 }
15397
15398 fn range_for_rename(
15399 &self,
15400 buffer: &Entity<Buffer>,
15401 position: text::Anchor,
15402 cx: &mut App,
15403 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
15404 Some(self.update(cx, |project, cx| {
15405 let buffer = buffer.clone();
15406 let task = project.prepare_rename(buffer.clone(), position, cx);
15407 cx.spawn(|_, mut cx| async move {
15408 Ok(match task.await? {
15409 PrepareRenameResponse::Success(range) => Some(range),
15410 PrepareRenameResponse::InvalidPosition => None,
15411 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
15412 // Fallback on using TreeSitter info to determine identifier range
15413 buffer.update(&mut cx, |buffer, _| {
15414 let snapshot = buffer.snapshot();
15415 let (range, kind) = snapshot.surrounding_word(position);
15416 if kind != Some(CharKind::Word) {
15417 return None;
15418 }
15419 Some(
15420 snapshot.anchor_before(range.start)
15421 ..snapshot.anchor_after(range.end),
15422 )
15423 })?
15424 }
15425 })
15426 })
15427 }))
15428 }
15429
15430 fn perform_rename(
15431 &self,
15432 buffer: &Entity<Buffer>,
15433 position: text::Anchor,
15434 new_name: String,
15435 cx: &mut App,
15436 ) -> Option<Task<Result<ProjectTransaction>>> {
15437 Some(self.update(cx, |project, cx| {
15438 project.perform_rename(buffer.clone(), position, new_name, cx)
15439 }))
15440 }
15441}
15442
15443fn inlay_hint_settings(
15444 location: Anchor,
15445 snapshot: &MultiBufferSnapshot,
15446 cx: &mut Context<Editor>,
15447) -> InlayHintSettings {
15448 let file = snapshot.file_at(location);
15449 let language = snapshot.language_at(location).map(|l| l.name());
15450 language_settings(language, file, cx).inlay_hints
15451}
15452
15453fn consume_contiguous_rows(
15454 contiguous_row_selections: &mut Vec<Selection<Point>>,
15455 selection: &Selection<Point>,
15456 display_map: &DisplaySnapshot,
15457 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
15458) -> (MultiBufferRow, MultiBufferRow) {
15459 contiguous_row_selections.push(selection.clone());
15460 let start_row = MultiBufferRow(selection.start.row);
15461 let mut end_row = ending_row(selection, display_map);
15462
15463 while let Some(next_selection) = selections.peek() {
15464 if next_selection.start.row <= end_row.0 {
15465 end_row = ending_row(next_selection, display_map);
15466 contiguous_row_selections.push(selections.next().unwrap().clone());
15467 } else {
15468 break;
15469 }
15470 }
15471 (start_row, end_row)
15472}
15473
15474fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
15475 if next_selection.end.column > 0 || next_selection.is_empty() {
15476 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
15477 } else {
15478 MultiBufferRow(next_selection.end.row)
15479 }
15480}
15481
15482impl EditorSnapshot {
15483 pub fn remote_selections_in_range<'a>(
15484 &'a self,
15485 range: &'a Range<Anchor>,
15486 collaboration_hub: &dyn CollaborationHub,
15487 cx: &'a App,
15488 ) -> impl 'a + Iterator<Item = RemoteSelection> {
15489 let participant_names = collaboration_hub.user_names(cx);
15490 let participant_indices = collaboration_hub.user_participant_indices(cx);
15491 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
15492 let collaborators_by_replica_id = collaborators_by_peer_id
15493 .iter()
15494 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
15495 .collect::<HashMap<_, _>>();
15496 self.buffer_snapshot
15497 .selections_in_range(range, false)
15498 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
15499 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
15500 let participant_index = participant_indices.get(&collaborator.user_id).copied();
15501 let user_name = participant_names.get(&collaborator.user_id).cloned();
15502 Some(RemoteSelection {
15503 replica_id,
15504 selection,
15505 cursor_shape,
15506 line_mode,
15507 participant_index,
15508 peer_id: collaborator.peer_id,
15509 user_name,
15510 })
15511 })
15512 }
15513
15514 pub fn hunks_for_ranges(
15515 &self,
15516 ranges: impl Iterator<Item = Range<Point>>,
15517 ) -> Vec<MultiBufferDiffHunk> {
15518 let mut hunks = Vec::new();
15519 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
15520 HashMap::default();
15521 for query_range in ranges {
15522 let query_rows =
15523 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
15524 for hunk in self.buffer_snapshot.diff_hunks_in_range(
15525 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
15526 ) {
15527 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
15528 // when the caret is just above or just below the deleted hunk.
15529 let allow_adjacent = hunk.status() == DiffHunkStatus::Removed;
15530 let related_to_selection = if allow_adjacent {
15531 hunk.row_range.overlaps(&query_rows)
15532 || hunk.row_range.start == query_rows.end
15533 || hunk.row_range.end == query_rows.start
15534 } else {
15535 hunk.row_range.overlaps(&query_rows)
15536 };
15537 if related_to_selection {
15538 if !processed_buffer_rows
15539 .entry(hunk.buffer_id)
15540 .or_default()
15541 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
15542 {
15543 continue;
15544 }
15545 hunks.push(hunk);
15546 }
15547 }
15548 }
15549
15550 hunks
15551 }
15552
15553 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
15554 self.display_snapshot.buffer_snapshot.language_at(position)
15555 }
15556
15557 pub fn is_focused(&self) -> bool {
15558 self.is_focused
15559 }
15560
15561 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
15562 self.placeholder_text.as_ref()
15563 }
15564
15565 pub fn scroll_position(&self) -> gpui::Point<f32> {
15566 self.scroll_anchor.scroll_position(&self.display_snapshot)
15567 }
15568
15569 fn gutter_dimensions(
15570 &self,
15571 font_id: FontId,
15572 font_size: Pixels,
15573 max_line_number_width: Pixels,
15574 cx: &App,
15575 ) -> Option<GutterDimensions> {
15576 if !self.show_gutter {
15577 return None;
15578 }
15579
15580 let descent = cx.text_system().descent(font_id, font_size);
15581 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
15582 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
15583
15584 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
15585 matches!(
15586 ProjectSettings::get_global(cx).git.git_gutter,
15587 Some(GitGutterSetting::TrackedFiles)
15588 )
15589 });
15590 let gutter_settings = EditorSettings::get_global(cx).gutter;
15591 let show_line_numbers = self
15592 .show_line_numbers
15593 .unwrap_or(gutter_settings.line_numbers);
15594 let line_gutter_width = if show_line_numbers {
15595 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
15596 let min_width_for_number_on_gutter = em_advance * 4.0;
15597 max_line_number_width.max(min_width_for_number_on_gutter)
15598 } else {
15599 0.0.into()
15600 };
15601
15602 let show_code_actions = self
15603 .show_code_actions
15604 .unwrap_or(gutter_settings.code_actions);
15605
15606 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
15607
15608 let git_blame_entries_width =
15609 self.git_blame_gutter_max_author_length
15610 .map(|max_author_length| {
15611 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
15612
15613 /// The number of characters to dedicate to gaps and margins.
15614 const SPACING_WIDTH: usize = 4;
15615
15616 let max_char_count = max_author_length
15617 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
15618 + ::git::SHORT_SHA_LENGTH
15619 + MAX_RELATIVE_TIMESTAMP.len()
15620 + SPACING_WIDTH;
15621
15622 em_advance * max_char_count
15623 });
15624
15625 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
15626 left_padding += if show_code_actions || show_runnables {
15627 em_width * 3.0
15628 } else if show_git_gutter && show_line_numbers {
15629 em_width * 2.0
15630 } else if show_git_gutter || show_line_numbers {
15631 em_width
15632 } else {
15633 px(0.)
15634 };
15635
15636 let right_padding = if gutter_settings.folds && show_line_numbers {
15637 em_width * 4.0
15638 } else if gutter_settings.folds {
15639 em_width * 3.0
15640 } else if show_line_numbers {
15641 em_width
15642 } else {
15643 px(0.)
15644 };
15645
15646 Some(GutterDimensions {
15647 left_padding,
15648 right_padding,
15649 width: line_gutter_width + left_padding + right_padding,
15650 margin: -descent,
15651 git_blame_entries_width,
15652 })
15653 }
15654
15655 pub fn render_crease_toggle(
15656 &self,
15657 buffer_row: MultiBufferRow,
15658 row_contains_cursor: bool,
15659 editor: Entity<Editor>,
15660 window: &mut Window,
15661 cx: &mut App,
15662 ) -> Option<AnyElement> {
15663 let folded = self.is_line_folded(buffer_row);
15664 let mut is_foldable = false;
15665
15666 if let Some(crease) = self
15667 .crease_snapshot
15668 .query_row(buffer_row, &self.buffer_snapshot)
15669 {
15670 is_foldable = true;
15671 match crease {
15672 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
15673 if let Some(render_toggle) = render_toggle {
15674 let toggle_callback =
15675 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
15676 if folded {
15677 editor.update(cx, |editor, cx| {
15678 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
15679 });
15680 } else {
15681 editor.update(cx, |editor, cx| {
15682 editor.unfold_at(
15683 &crate::UnfoldAt { buffer_row },
15684 window,
15685 cx,
15686 )
15687 });
15688 }
15689 });
15690 return Some((render_toggle)(
15691 buffer_row,
15692 folded,
15693 toggle_callback,
15694 window,
15695 cx,
15696 ));
15697 }
15698 }
15699 }
15700 }
15701
15702 is_foldable |= self.starts_indent(buffer_row);
15703
15704 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
15705 Some(
15706 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
15707 .toggle_state(folded)
15708 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
15709 if folded {
15710 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
15711 } else {
15712 this.fold_at(&FoldAt { buffer_row }, window, cx);
15713 }
15714 }))
15715 .into_any_element(),
15716 )
15717 } else {
15718 None
15719 }
15720 }
15721
15722 pub fn render_crease_trailer(
15723 &self,
15724 buffer_row: MultiBufferRow,
15725 window: &mut Window,
15726 cx: &mut App,
15727 ) -> Option<AnyElement> {
15728 let folded = self.is_line_folded(buffer_row);
15729 if let Crease::Inline { render_trailer, .. } = self
15730 .crease_snapshot
15731 .query_row(buffer_row, &self.buffer_snapshot)?
15732 {
15733 let render_trailer = render_trailer.as_ref()?;
15734 Some(render_trailer(buffer_row, folded, window, cx))
15735 } else {
15736 None
15737 }
15738 }
15739}
15740
15741impl Deref for EditorSnapshot {
15742 type Target = DisplaySnapshot;
15743
15744 fn deref(&self) -> &Self::Target {
15745 &self.display_snapshot
15746 }
15747}
15748
15749#[derive(Clone, Debug, PartialEq, Eq)]
15750pub enum EditorEvent {
15751 InputIgnored {
15752 text: Arc<str>,
15753 },
15754 InputHandled {
15755 utf16_range_to_replace: Option<Range<isize>>,
15756 text: Arc<str>,
15757 },
15758 ExcerptsAdded {
15759 buffer: Entity<Buffer>,
15760 predecessor: ExcerptId,
15761 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
15762 },
15763 ExcerptsRemoved {
15764 ids: Vec<ExcerptId>,
15765 },
15766 BufferFoldToggled {
15767 ids: Vec<ExcerptId>,
15768 folded: bool,
15769 },
15770 ExcerptsEdited {
15771 ids: Vec<ExcerptId>,
15772 },
15773 ExcerptsExpanded {
15774 ids: Vec<ExcerptId>,
15775 },
15776 BufferEdited,
15777 Edited {
15778 transaction_id: clock::Lamport,
15779 },
15780 Reparsed(BufferId),
15781 Focused,
15782 FocusedIn,
15783 Blurred,
15784 DirtyChanged,
15785 Saved,
15786 TitleChanged,
15787 DiffBaseChanged,
15788 SelectionsChanged {
15789 local: bool,
15790 },
15791 ScrollPositionChanged {
15792 local: bool,
15793 autoscroll: bool,
15794 },
15795 Closed,
15796 TransactionUndone {
15797 transaction_id: clock::Lamport,
15798 },
15799 TransactionBegun {
15800 transaction_id: clock::Lamport,
15801 },
15802 Reloaded,
15803 CursorShapeChanged,
15804}
15805
15806impl EventEmitter<EditorEvent> for Editor {}
15807
15808impl Focusable for Editor {
15809 fn focus_handle(&self, _cx: &App) -> FocusHandle {
15810 self.focus_handle.clone()
15811 }
15812}
15813
15814impl Render for Editor {
15815 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
15816 let settings = ThemeSettings::get_global(cx);
15817
15818 let mut text_style = match self.mode {
15819 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
15820 color: cx.theme().colors().editor_foreground,
15821 font_family: settings.ui_font.family.clone(),
15822 font_features: settings.ui_font.features.clone(),
15823 font_fallbacks: settings.ui_font.fallbacks.clone(),
15824 font_size: rems(0.875).into(),
15825 font_weight: settings.ui_font.weight,
15826 line_height: relative(settings.buffer_line_height.value()),
15827 ..Default::default()
15828 },
15829 EditorMode::Full => TextStyle {
15830 color: cx.theme().colors().editor_foreground,
15831 font_family: settings.buffer_font.family.clone(),
15832 font_features: settings.buffer_font.features.clone(),
15833 font_fallbacks: settings.buffer_font.fallbacks.clone(),
15834 font_size: settings.buffer_font_size().into(),
15835 font_weight: settings.buffer_font.weight,
15836 line_height: relative(settings.buffer_line_height.value()),
15837 ..Default::default()
15838 },
15839 };
15840 if let Some(text_style_refinement) = &self.text_style_refinement {
15841 text_style.refine(text_style_refinement)
15842 }
15843
15844 let background = match self.mode {
15845 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
15846 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
15847 EditorMode::Full => cx.theme().colors().editor_background,
15848 };
15849
15850 EditorElement::new(
15851 &cx.entity(),
15852 EditorStyle {
15853 background,
15854 local_player: cx.theme().players().local(),
15855 text: text_style,
15856 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
15857 syntax: cx.theme().syntax().clone(),
15858 status: cx.theme().status().clone(),
15859 inlay_hints_style: make_inlay_hints_style(cx),
15860 inline_completion_styles: make_suggestion_styles(cx),
15861 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
15862 },
15863 )
15864 }
15865}
15866
15867impl EntityInputHandler for Editor {
15868 fn text_for_range(
15869 &mut self,
15870 range_utf16: Range<usize>,
15871 adjusted_range: &mut Option<Range<usize>>,
15872 _: &mut Window,
15873 cx: &mut Context<Self>,
15874 ) -> Option<String> {
15875 let snapshot = self.buffer.read(cx).read(cx);
15876 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
15877 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
15878 if (start.0..end.0) != range_utf16 {
15879 adjusted_range.replace(start.0..end.0);
15880 }
15881 Some(snapshot.text_for_range(start..end).collect())
15882 }
15883
15884 fn selected_text_range(
15885 &mut self,
15886 ignore_disabled_input: bool,
15887 _: &mut Window,
15888 cx: &mut Context<Self>,
15889 ) -> Option<UTF16Selection> {
15890 // Prevent the IME menu from appearing when holding down an alphabetic key
15891 // while input is disabled.
15892 if !ignore_disabled_input && !self.input_enabled {
15893 return None;
15894 }
15895
15896 let selection = self.selections.newest::<OffsetUtf16>(cx);
15897 let range = selection.range();
15898
15899 Some(UTF16Selection {
15900 range: range.start.0..range.end.0,
15901 reversed: selection.reversed,
15902 })
15903 }
15904
15905 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
15906 let snapshot = self.buffer.read(cx).read(cx);
15907 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
15908 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
15909 }
15910
15911 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
15912 self.clear_highlights::<InputComposition>(cx);
15913 self.ime_transaction.take();
15914 }
15915
15916 fn replace_text_in_range(
15917 &mut self,
15918 range_utf16: Option<Range<usize>>,
15919 text: &str,
15920 window: &mut Window,
15921 cx: &mut Context<Self>,
15922 ) {
15923 if !self.input_enabled {
15924 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15925 return;
15926 }
15927
15928 self.transact(window, cx, |this, window, cx| {
15929 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
15930 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
15931 Some(this.selection_replacement_ranges(range_utf16, cx))
15932 } else {
15933 this.marked_text_ranges(cx)
15934 };
15935
15936 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
15937 let newest_selection_id = this.selections.newest_anchor().id;
15938 this.selections
15939 .all::<OffsetUtf16>(cx)
15940 .iter()
15941 .zip(ranges_to_replace.iter())
15942 .find_map(|(selection, range)| {
15943 if selection.id == newest_selection_id {
15944 Some(
15945 (range.start.0 as isize - selection.head().0 as isize)
15946 ..(range.end.0 as isize - selection.head().0 as isize),
15947 )
15948 } else {
15949 None
15950 }
15951 })
15952 });
15953
15954 cx.emit(EditorEvent::InputHandled {
15955 utf16_range_to_replace: range_to_replace,
15956 text: text.into(),
15957 });
15958
15959 if let Some(new_selected_ranges) = new_selected_ranges {
15960 this.change_selections(None, window, cx, |selections| {
15961 selections.select_ranges(new_selected_ranges)
15962 });
15963 this.backspace(&Default::default(), window, cx);
15964 }
15965
15966 this.handle_input(text, window, cx);
15967 });
15968
15969 if let Some(transaction) = self.ime_transaction {
15970 self.buffer.update(cx, |buffer, cx| {
15971 buffer.group_until_transaction(transaction, cx);
15972 });
15973 }
15974
15975 self.unmark_text(window, cx);
15976 }
15977
15978 fn replace_and_mark_text_in_range(
15979 &mut self,
15980 range_utf16: Option<Range<usize>>,
15981 text: &str,
15982 new_selected_range_utf16: Option<Range<usize>>,
15983 window: &mut Window,
15984 cx: &mut Context<Self>,
15985 ) {
15986 if !self.input_enabled {
15987 return;
15988 }
15989
15990 let transaction = self.transact(window, cx, |this, window, cx| {
15991 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
15992 let snapshot = this.buffer.read(cx).read(cx);
15993 if let Some(relative_range_utf16) = range_utf16.as_ref() {
15994 for marked_range in &mut marked_ranges {
15995 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
15996 marked_range.start.0 += relative_range_utf16.start;
15997 marked_range.start =
15998 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
15999 marked_range.end =
16000 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
16001 }
16002 }
16003 Some(marked_ranges)
16004 } else if let Some(range_utf16) = range_utf16 {
16005 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
16006 Some(this.selection_replacement_ranges(range_utf16, cx))
16007 } else {
16008 None
16009 };
16010
16011 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
16012 let newest_selection_id = this.selections.newest_anchor().id;
16013 this.selections
16014 .all::<OffsetUtf16>(cx)
16015 .iter()
16016 .zip(ranges_to_replace.iter())
16017 .find_map(|(selection, range)| {
16018 if selection.id == newest_selection_id {
16019 Some(
16020 (range.start.0 as isize - selection.head().0 as isize)
16021 ..(range.end.0 as isize - selection.head().0 as isize),
16022 )
16023 } else {
16024 None
16025 }
16026 })
16027 });
16028
16029 cx.emit(EditorEvent::InputHandled {
16030 utf16_range_to_replace: range_to_replace,
16031 text: text.into(),
16032 });
16033
16034 if let Some(ranges) = ranges_to_replace {
16035 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
16036 }
16037
16038 let marked_ranges = {
16039 let snapshot = this.buffer.read(cx).read(cx);
16040 this.selections
16041 .disjoint_anchors()
16042 .iter()
16043 .map(|selection| {
16044 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
16045 })
16046 .collect::<Vec<_>>()
16047 };
16048
16049 if text.is_empty() {
16050 this.unmark_text(window, cx);
16051 } else {
16052 this.highlight_text::<InputComposition>(
16053 marked_ranges.clone(),
16054 HighlightStyle {
16055 underline: Some(UnderlineStyle {
16056 thickness: px(1.),
16057 color: None,
16058 wavy: false,
16059 }),
16060 ..Default::default()
16061 },
16062 cx,
16063 );
16064 }
16065
16066 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
16067 let use_autoclose = this.use_autoclose;
16068 let use_auto_surround = this.use_auto_surround;
16069 this.set_use_autoclose(false);
16070 this.set_use_auto_surround(false);
16071 this.handle_input(text, window, cx);
16072 this.set_use_autoclose(use_autoclose);
16073 this.set_use_auto_surround(use_auto_surround);
16074
16075 if let Some(new_selected_range) = new_selected_range_utf16 {
16076 let snapshot = this.buffer.read(cx).read(cx);
16077 let new_selected_ranges = marked_ranges
16078 .into_iter()
16079 .map(|marked_range| {
16080 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
16081 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
16082 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
16083 snapshot.clip_offset_utf16(new_start, Bias::Left)
16084 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
16085 })
16086 .collect::<Vec<_>>();
16087
16088 drop(snapshot);
16089 this.change_selections(None, window, cx, |selections| {
16090 selections.select_ranges(new_selected_ranges)
16091 });
16092 }
16093 });
16094
16095 self.ime_transaction = self.ime_transaction.or(transaction);
16096 if let Some(transaction) = self.ime_transaction {
16097 self.buffer.update(cx, |buffer, cx| {
16098 buffer.group_until_transaction(transaction, cx);
16099 });
16100 }
16101
16102 if self.text_highlights::<InputComposition>(cx).is_none() {
16103 self.ime_transaction.take();
16104 }
16105 }
16106
16107 fn bounds_for_range(
16108 &mut self,
16109 range_utf16: Range<usize>,
16110 element_bounds: gpui::Bounds<Pixels>,
16111 window: &mut Window,
16112 cx: &mut Context<Self>,
16113 ) -> Option<gpui::Bounds<Pixels>> {
16114 let text_layout_details = self.text_layout_details(window);
16115 let gpui::Size {
16116 width: em_width,
16117 height: line_height,
16118 } = self.character_size(window);
16119
16120 let snapshot = self.snapshot(window, cx);
16121 let scroll_position = snapshot.scroll_position();
16122 let scroll_left = scroll_position.x * em_width;
16123
16124 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
16125 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
16126 + self.gutter_dimensions.width
16127 + self.gutter_dimensions.margin;
16128 let y = line_height * (start.row().as_f32() - scroll_position.y);
16129
16130 Some(Bounds {
16131 origin: element_bounds.origin + point(x, y),
16132 size: size(em_width, line_height),
16133 })
16134 }
16135
16136 fn character_index_for_point(
16137 &mut self,
16138 point: gpui::Point<Pixels>,
16139 _window: &mut Window,
16140 _cx: &mut Context<Self>,
16141 ) -> Option<usize> {
16142 let position_map = self.last_position_map.as_ref()?;
16143 if !position_map.text_hitbox.contains(&point) {
16144 return None;
16145 }
16146 let display_point = position_map.point_for_position(point).previous_valid;
16147 let anchor = position_map
16148 .snapshot
16149 .display_point_to_anchor(display_point, Bias::Left);
16150 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
16151 Some(utf16_offset.0)
16152 }
16153}
16154
16155trait SelectionExt {
16156 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
16157 fn spanned_rows(
16158 &self,
16159 include_end_if_at_line_start: bool,
16160 map: &DisplaySnapshot,
16161 ) -> Range<MultiBufferRow>;
16162}
16163
16164impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
16165 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
16166 let start = self
16167 .start
16168 .to_point(&map.buffer_snapshot)
16169 .to_display_point(map);
16170 let end = self
16171 .end
16172 .to_point(&map.buffer_snapshot)
16173 .to_display_point(map);
16174 if self.reversed {
16175 end..start
16176 } else {
16177 start..end
16178 }
16179 }
16180
16181 fn spanned_rows(
16182 &self,
16183 include_end_if_at_line_start: bool,
16184 map: &DisplaySnapshot,
16185 ) -> Range<MultiBufferRow> {
16186 let start = self.start.to_point(&map.buffer_snapshot);
16187 let mut end = self.end.to_point(&map.buffer_snapshot);
16188 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
16189 end.row -= 1;
16190 }
16191
16192 let buffer_start = map.prev_line_boundary(start).0;
16193 let buffer_end = map.next_line_boundary(end).0;
16194 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
16195 }
16196}
16197
16198impl<T: InvalidationRegion> InvalidationStack<T> {
16199 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
16200 where
16201 S: Clone + ToOffset,
16202 {
16203 while let Some(region) = self.last() {
16204 let all_selections_inside_invalidation_ranges =
16205 if selections.len() == region.ranges().len() {
16206 selections
16207 .iter()
16208 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
16209 .all(|(selection, invalidation_range)| {
16210 let head = selection.head().to_offset(buffer);
16211 invalidation_range.start <= head && invalidation_range.end >= head
16212 })
16213 } else {
16214 false
16215 };
16216
16217 if all_selections_inside_invalidation_ranges {
16218 break;
16219 } else {
16220 self.pop();
16221 }
16222 }
16223 }
16224}
16225
16226impl<T> Default for InvalidationStack<T> {
16227 fn default() -> Self {
16228 Self(Default::default())
16229 }
16230}
16231
16232impl<T> Deref for InvalidationStack<T> {
16233 type Target = Vec<T>;
16234
16235 fn deref(&self) -> &Self::Target {
16236 &self.0
16237 }
16238}
16239
16240impl<T> DerefMut for InvalidationStack<T> {
16241 fn deref_mut(&mut self) -> &mut Self::Target {
16242 &mut self.0
16243 }
16244}
16245
16246impl InvalidationRegion for SnippetState {
16247 fn ranges(&self) -> &[Range<Anchor>] {
16248 &self.ranges[self.active_index]
16249 }
16250}
16251
16252pub fn diagnostic_block_renderer(
16253 diagnostic: Diagnostic,
16254 max_message_rows: Option<u8>,
16255 allow_closing: bool,
16256 _is_valid: bool,
16257) -> RenderBlock {
16258 let (text_without_backticks, code_ranges) =
16259 highlight_diagnostic_message(&diagnostic, max_message_rows);
16260
16261 Arc::new(move |cx: &mut BlockContext| {
16262 let group_id: SharedString = cx.block_id.to_string().into();
16263
16264 let mut text_style = cx.window.text_style().clone();
16265 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
16266 let theme_settings = ThemeSettings::get_global(cx);
16267 text_style.font_family = theme_settings.buffer_font.family.clone();
16268 text_style.font_style = theme_settings.buffer_font.style;
16269 text_style.font_features = theme_settings.buffer_font.features.clone();
16270 text_style.font_weight = theme_settings.buffer_font.weight;
16271
16272 let multi_line_diagnostic = diagnostic.message.contains('\n');
16273
16274 let buttons = |diagnostic: &Diagnostic| {
16275 if multi_line_diagnostic {
16276 v_flex()
16277 } else {
16278 h_flex()
16279 }
16280 .when(allow_closing, |div| {
16281 div.children(diagnostic.is_primary.then(|| {
16282 IconButton::new("close-block", IconName::XCircle)
16283 .icon_color(Color::Muted)
16284 .size(ButtonSize::Compact)
16285 .style(ButtonStyle::Transparent)
16286 .visible_on_hover(group_id.clone())
16287 .on_click(move |_click, window, cx| {
16288 window.dispatch_action(Box::new(Cancel), cx)
16289 })
16290 .tooltip(|window, cx| {
16291 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
16292 })
16293 }))
16294 })
16295 .child(
16296 IconButton::new("copy-block", IconName::Copy)
16297 .icon_color(Color::Muted)
16298 .size(ButtonSize::Compact)
16299 .style(ButtonStyle::Transparent)
16300 .visible_on_hover(group_id.clone())
16301 .on_click({
16302 let message = diagnostic.message.clone();
16303 move |_click, _, cx| {
16304 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
16305 }
16306 })
16307 .tooltip(Tooltip::text("Copy diagnostic message")),
16308 )
16309 };
16310
16311 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
16312 AvailableSpace::min_size(),
16313 cx.window,
16314 cx.app,
16315 );
16316
16317 h_flex()
16318 .id(cx.block_id)
16319 .group(group_id.clone())
16320 .relative()
16321 .size_full()
16322 .block_mouse_down()
16323 .pl(cx.gutter_dimensions.width)
16324 .w(cx.max_width - cx.gutter_dimensions.full_width())
16325 .child(
16326 div()
16327 .flex()
16328 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
16329 .flex_shrink(),
16330 )
16331 .child(buttons(&diagnostic))
16332 .child(div().flex().flex_shrink_0().child(
16333 StyledText::new(text_without_backticks.clone()).with_highlights(
16334 &text_style,
16335 code_ranges.iter().map(|range| {
16336 (
16337 range.clone(),
16338 HighlightStyle {
16339 font_weight: Some(FontWeight::BOLD),
16340 ..Default::default()
16341 },
16342 )
16343 }),
16344 ),
16345 ))
16346 .into_any_element()
16347 })
16348}
16349
16350fn inline_completion_edit_text(
16351 current_snapshot: &BufferSnapshot,
16352 edits: &[(Range<Anchor>, String)],
16353 edit_preview: &EditPreview,
16354 include_deletions: bool,
16355 cx: &App,
16356) -> HighlightedText {
16357 let edits = edits
16358 .iter()
16359 .map(|(anchor, text)| {
16360 (
16361 anchor.start.text_anchor..anchor.end.text_anchor,
16362 text.clone(),
16363 )
16364 })
16365 .collect::<Vec<_>>();
16366
16367 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
16368}
16369
16370pub fn highlight_diagnostic_message(
16371 diagnostic: &Diagnostic,
16372 mut max_message_rows: Option<u8>,
16373) -> (SharedString, Vec<Range<usize>>) {
16374 let mut text_without_backticks = String::new();
16375 let mut code_ranges = Vec::new();
16376
16377 if let Some(source) = &diagnostic.source {
16378 text_without_backticks.push_str(source);
16379 code_ranges.push(0..source.len());
16380 text_without_backticks.push_str(": ");
16381 }
16382
16383 let mut prev_offset = 0;
16384 let mut in_code_block = false;
16385 let has_row_limit = max_message_rows.is_some();
16386 let mut newline_indices = diagnostic
16387 .message
16388 .match_indices('\n')
16389 .filter(|_| has_row_limit)
16390 .map(|(ix, _)| ix)
16391 .fuse()
16392 .peekable();
16393
16394 for (quote_ix, _) in diagnostic
16395 .message
16396 .match_indices('`')
16397 .chain([(diagnostic.message.len(), "")])
16398 {
16399 let mut first_newline_ix = None;
16400 let mut last_newline_ix = None;
16401 while let Some(newline_ix) = newline_indices.peek() {
16402 if *newline_ix < quote_ix {
16403 if first_newline_ix.is_none() {
16404 first_newline_ix = Some(*newline_ix);
16405 }
16406 last_newline_ix = Some(*newline_ix);
16407
16408 if let Some(rows_left) = &mut max_message_rows {
16409 if *rows_left == 0 {
16410 break;
16411 } else {
16412 *rows_left -= 1;
16413 }
16414 }
16415 let _ = newline_indices.next();
16416 } else {
16417 break;
16418 }
16419 }
16420 let prev_len = text_without_backticks.len();
16421 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
16422 text_without_backticks.push_str(new_text);
16423 if in_code_block {
16424 code_ranges.push(prev_len..text_without_backticks.len());
16425 }
16426 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
16427 in_code_block = !in_code_block;
16428 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
16429 text_without_backticks.push_str("...");
16430 break;
16431 }
16432 }
16433
16434 (text_without_backticks.into(), code_ranges)
16435}
16436
16437fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
16438 match severity {
16439 DiagnosticSeverity::ERROR => colors.error,
16440 DiagnosticSeverity::WARNING => colors.warning,
16441 DiagnosticSeverity::INFORMATION => colors.info,
16442 DiagnosticSeverity::HINT => colors.info,
16443 _ => colors.ignored,
16444 }
16445}
16446
16447pub fn styled_runs_for_code_label<'a>(
16448 label: &'a CodeLabel,
16449 syntax_theme: &'a theme::SyntaxTheme,
16450) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
16451 let fade_out = HighlightStyle {
16452 fade_out: Some(0.35),
16453 ..Default::default()
16454 };
16455
16456 let mut prev_end = label.filter_range.end;
16457 label
16458 .runs
16459 .iter()
16460 .enumerate()
16461 .flat_map(move |(ix, (range, highlight_id))| {
16462 let style = if let Some(style) = highlight_id.style(syntax_theme) {
16463 style
16464 } else {
16465 return Default::default();
16466 };
16467 let mut muted_style = style;
16468 muted_style.highlight(fade_out);
16469
16470 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
16471 if range.start >= label.filter_range.end {
16472 if range.start > prev_end {
16473 runs.push((prev_end..range.start, fade_out));
16474 }
16475 runs.push((range.clone(), muted_style));
16476 } else if range.end <= label.filter_range.end {
16477 runs.push((range.clone(), style));
16478 } else {
16479 runs.push((range.start..label.filter_range.end, style));
16480 runs.push((label.filter_range.end..range.end, muted_style));
16481 }
16482 prev_end = cmp::max(prev_end, range.end);
16483
16484 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
16485 runs.push((prev_end..label.text.len(), fade_out));
16486 }
16487
16488 runs
16489 })
16490}
16491
16492pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
16493 let mut prev_index = 0;
16494 let mut prev_codepoint: Option<char> = None;
16495 text.char_indices()
16496 .chain([(text.len(), '\0')])
16497 .filter_map(move |(index, codepoint)| {
16498 let prev_codepoint = prev_codepoint.replace(codepoint)?;
16499 let is_boundary = index == text.len()
16500 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
16501 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
16502 if is_boundary {
16503 let chunk = &text[prev_index..index];
16504 prev_index = index;
16505 Some(chunk)
16506 } else {
16507 None
16508 }
16509 })
16510}
16511
16512pub trait RangeToAnchorExt: Sized {
16513 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
16514
16515 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
16516 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
16517 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
16518 }
16519}
16520
16521impl<T: ToOffset> RangeToAnchorExt for Range<T> {
16522 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
16523 let start_offset = self.start.to_offset(snapshot);
16524 let end_offset = self.end.to_offset(snapshot);
16525 if start_offset == end_offset {
16526 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
16527 } else {
16528 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
16529 }
16530 }
16531}
16532
16533pub trait RowExt {
16534 fn as_f32(&self) -> f32;
16535
16536 fn next_row(&self) -> Self;
16537
16538 fn previous_row(&self) -> Self;
16539
16540 fn minus(&self, other: Self) -> u32;
16541}
16542
16543impl RowExt for DisplayRow {
16544 fn as_f32(&self) -> f32 {
16545 self.0 as f32
16546 }
16547
16548 fn next_row(&self) -> Self {
16549 Self(self.0 + 1)
16550 }
16551
16552 fn previous_row(&self) -> Self {
16553 Self(self.0.saturating_sub(1))
16554 }
16555
16556 fn minus(&self, other: Self) -> u32 {
16557 self.0 - other.0
16558 }
16559}
16560
16561impl RowExt for MultiBufferRow {
16562 fn as_f32(&self) -> f32 {
16563 self.0 as f32
16564 }
16565
16566 fn next_row(&self) -> Self {
16567 Self(self.0 + 1)
16568 }
16569
16570 fn previous_row(&self) -> Self {
16571 Self(self.0.saturating_sub(1))
16572 }
16573
16574 fn minus(&self, other: Self) -> u32 {
16575 self.0 - other.0
16576 }
16577}
16578
16579trait RowRangeExt {
16580 type Row;
16581
16582 fn len(&self) -> usize;
16583
16584 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
16585}
16586
16587impl RowRangeExt for Range<MultiBufferRow> {
16588 type Row = MultiBufferRow;
16589
16590 fn len(&self) -> usize {
16591 (self.end.0 - self.start.0) as usize
16592 }
16593
16594 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
16595 (self.start.0..self.end.0).map(MultiBufferRow)
16596 }
16597}
16598
16599impl RowRangeExt for Range<DisplayRow> {
16600 type Row = DisplayRow;
16601
16602 fn len(&self) -> usize {
16603 (self.end.0 - self.start.0) as usize
16604 }
16605
16606 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
16607 (self.start.0..self.end.0).map(DisplayRow)
16608 }
16609}
16610
16611/// If select range has more than one line, we
16612/// just point the cursor to range.start.
16613fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
16614 if range.start.row == range.end.row {
16615 range
16616 } else {
16617 range.start..range.start
16618 }
16619}
16620pub struct KillRing(ClipboardItem);
16621impl Global for KillRing {}
16622
16623const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
16624
16625fn all_edits_insertions_or_deletions(
16626 edits: &Vec<(Range<Anchor>, String)>,
16627 snapshot: &MultiBufferSnapshot,
16628) -> bool {
16629 let mut all_insertions = true;
16630 let mut all_deletions = true;
16631
16632 for (range, new_text) in edits.iter() {
16633 let range_is_empty = range.to_offset(&snapshot).is_empty();
16634 let text_is_empty = new_text.is_empty();
16635
16636 if range_is_empty != text_is_empty {
16637 if range_is_empty {
16638 all_deletions = false;
16639 } else {
16640 all_insertions = false;
16641 }
16642 } else {
16643 return false;
16644 }
16645
16646 if !all_insertions && !all_deletions {
16647 return false;
16648 }
16649 }
16650 all_insertions || all_deletions
16651}