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
50use ::git::diff::DiffHunkStatus;
51pub(crate) use actions::*;
52pub use actions::{OpenExcerpts, OpenExcerptsSplit};
53use aho_corasick::AhoCorasick;
54use anyhow::{anyhow, Context as _, Result};
55use blink_manager::BlinkManager;
56use client::{Collaborator, ParticipantIndex};
57use clock::ReplicaId;
58use collections::{BTreeMap, HashMap, HashSet, VecDeque};
59use convert_case::{Case, Casing};
60use display_map::*;
61pub use display_map::{DisplayPoint, FoldPlaceholder};
62pub use editor_settings::{
63 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
64};
65pub use editor_settings_controls::*;
66use element::LineWithInvisibles;
67pub use element::{
68 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
69};
70use futures::{future, FutureExt};
71use fuzzy::StringMatchCandidate;
72
73use code_context_menus::{
74 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
75 CompletionsMenu, ContextMenuOrigin,
76};
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, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
86 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::{InlineCompletionProvider, 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, Language, OffsetRangeExt, Point, Selection, SelectionGoal, TextObject,
101 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 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
128};
129use project::{
130 lsp_store::{FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
131 project_settings::{GitGutterSetting, ProjectSettings},
132 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
133 LspStore, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
134};
135use rand::prelude::*;
136use rpc::{proto::*, ErrorExt};
137use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
138use selections_collection::{
139 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
140};
141use serde::{Deserialize, Serialize};
142use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
143use smallvec::SmallVec;
144use snippet::Snippet;
145use std::{
146 any::TypeId,
147 borrow::Cow,
148 cell::RefCell,
149 cmp::{self, Ordering, Reverse},
150 mem,
151 num::NonZeroU32,
152 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
153 path::{Path, PathBuf},
154 rc::Rc,
155 sync::Arc,
156 time::{Duration, Instant},
157};
158pub use sum_tree::Bias;
159use sum_tree::TreeMap;
160use text::{BufferId, OffsetUtf16, Rope};
161use theme::{ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings};
162use ui::{
163 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
164 Tooltip,
165};
166use util::{defer, maybe, post_inc, RangeExt, ResultExt, TakeUntilExt, TryFutureExt};
167use workspace::item::{ItemHandle, PreviewTabsSettings};
168use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
169use workspace::{
170 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
171};
172use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
173
174use crate::hover_links::{find_url, find_url_from_range};
175use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
176
177pub const FILE_HEADER_HEIGHT: u32 = 2;
178pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
179pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
180pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
181const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
182const MAX_LINE_LEN: usize = 1024;
183const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
184const MAX_SELECTION_HISTORY_LEN: usize = 1024;
185pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
186#[doc(hidden)]
187pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
188
189pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
190pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
191
192pub fn render_parsed_markdown(
193 element_id: impl Into<ElementId>,
194 parsed: &language::ParsedMarkdown,
195 editor_style: &EditorStyle,
196 workspace: Option<WeakEntity<Workspace>>,
197 cx: &mut App,
198) -> InteractiveText {
199 let code_span_background_color = cx
200 .theme()
201 .colors()
202 .editor_document_highlight_read_background;
203
204 let highlights = gpui::combine_highlights(
205 parsed.highlights.iter().filter_map(|(range, highlight)| {
206 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
207 Some((range.clone(), highlight))
208 }),
209 parsed
210 .regions
211 .iter()
212 .zip(&parsed.region_ranges)
213 .filter_map(|(region, range)| {
214 if region.code {
215 Some((
216 range.clone(),
217 HighlightStyle {
218 background_color: Some(code_span_background_color),
219 ..Default::default()
220 },
221 ))
222 } else {
223 None
224 }
225 }),
226 );
227
228 let mut links = Vec::new();
229 let mut link_ranges = Vec::new();
230 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
231 if let Some(link) = region.link.clone() {
232 links.push(link);
233 link_ranges.push(range.clone());
234 }
235 }
236
237 InteractiveText::new(
238 element_id,
239 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
240 )
241 .on_click(
242 link_ranges,
243 move |clicked_range_ix, window, cx| match &links[clicked_range_ix] {
244 markdown::Link::Web { url } => cx.open_url(url),
245 markdown::Link::Path { path } => {
246 if let Some(workspace) = &workspace {
247 _ = workspace.update(cx, |workspace, cx| {
248 workspace
249 .open_abs_path(path.clone(), false, window, cx)
250 .detach();
251 });
252 }
253 }
254 },
255 )
256}
257
258#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
259pub enum InlayId {
260 InlineCompletion(usize),
261 Hint(usize),
262}
263
264impl InlayId {
265 fn id(&self) -> usize {
266 match self {
267 Self::InlineCompletion(id) => *id,
268 Self::Hint(id) => *id,
269 }
270 }
271}
272
273enum DocumentHighlightRead {}
274enum DocumentHighlightWrite {}
275enum InputComposition {}
276
277#[derive(Debug, Copy, Clone, PartialEq, Eq)]
278pub enum Navigated {
279 Yes,
280 No,
281}
282
283impl Navigated {
284 pub fn from_bool(yes: bool) -> Navigated {
285 if yes {
286 Navigated::Yes
287 } else {
288 Navigated::No
289 }
290 }
291}
292
293pub fn init_settings(cx: &mut App) {
294 EditorSettings::register(cx);
295}
296
297pub fn init(cx: &mut App) {
298 init_settings(cx);
299
300 workspace::register_project_item::<Editor>(cx);
301 workspace::FollowableViewRegistry::register::<Editor>(cx);
302 workspace::register_serializable_item::<Editor>(cx);
303
304 cx.observe_new(
305 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
306 workspace.register_action(Editor::new_file);
307 workspace.register_action(Editor::new_file_vertical);
308 workspace.register_action(Editor::new_file_horizontal);
309 },
310 )
311 .detach();
312
313 cx.on_action(move |_: &workspace::NewFile, cx| {
314 let app_state = workspace::AppState::global(cx);
315 if let Some(app_state) = app_state.upgrade() {
316 workspace::open_new(
317 Default::default(),
318 app_state,
319 cx,
320 |workspace, window, cx| {
321 Editor::new_file(workspace, &Default::default(), window, cx)
322 },
323 )
324 .detach();
325 }
326 });
327 cx.on_action(move |_: &workspace::NewWindow, cx| {
328 let app_state = workspace::AppState::global(cx);
329 if let Some(app_state) = app_state.upgrade() {
330 workspace::open_new(
331 Default::default(),
332 app_state,
333 cx,
334 |workspace, window, cx| {
335 cx.activate(true);
336 Editor::new_file(workspace, &Default::default(), window, cx)
337 },
338 )
339 .detach();
340 }
341 });
342}
343
344pub struct SearchWithinRange;
345
346trait InvalidationRegion {
347 fn ranges(&self) -> &[Range<Anchor>];
348}
349
350#[derive(Clone, Debug, PartialEq)]
351pub enum SelectPhase {
352 Begin {
353 position: DisplayPoint,
354 add: bool,
355 click_count: usize,
356 },
357 BeginColumnar {
358 position: DisplayPoint,
359 reset: bool,
360 goal_column: u32,
361 },
362 Extend {
363 position: DisplayPoint,
364 click_count: usize,
365 },
366 Update {
367 position: DisplayPoint,
368 goal_column: u32,
369 scroll_delta: gpui::Point<f32>,
370 },
371 End,
372}
373
374#[derive(Clone, Debug)]
375pub enum SelectMode {
376 Character,
377 Word(Range<Anchor>),
378 Line(Range<Anchor>),
379 All,
380}
381
382#[derive(Copy, Clone, PartialEq, Eq, Debug)]
383pub enum EditorMode {
384 SingleLine { auto_width: bool },
385 AutoHeight { max_lines: usize },
386 Full,
387}
388
389#[derive(Copy, Clone, Debug)]
390pub enum SoftWrap {
391 /// Prefer not to wrap at all.
392 ///
393 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
394 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
395 GitDiff,
396 /// Prefer a single line generally, unless an overly long line is encountered.
397 None,
398 /// Soft wrap lines that exceed the editor width.
399 EditorWidth,
400 /// Soft wrap lines at the preferred line length.
401 Column(u32),
402 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
403 Bounded(u32),
404}
405
406#[derive(Clone)]
407pub struct EditorStyle {
408 pub background: Hsla,
409 pub local_player: PlayerColor,
410 pub text: TextStyle,
411 pub scrollbar_width: Pixels,
412 pub syntax: Arc<SyntaxTheme>,
413 pub status: StatusColors,
414 pub inlay_hints_style: HighlightStyle,
415 pub inline_completion_styles: InlineCompletionStyles,
416 pub unnecessary_code_fade: f32,
417}
418
419impl Default for EditorStyle {
420 fn default() -> Self {
421 Self {
422 background: Hsla::default(),
423 local_player: PlayerColor::default(),
424 text: TextStyle::default(),
425 scrollbar_width: Pixels::default(),
426 syntax: Default::default(),
427 // HACK: Status colors don't have a real default.
428 // We should look into removing the status colors from the editor
429 // style and retrieve them directly from the theme.
430 status: StatusColors::dark(),
431 inlay_hints_style: HighlightStyle::default(),
432 inline_completion_styles: InlineCompletionStyles {
433 insertion: HighlightStyle::default(),
434 whitespace: HighlightStyle::default(),
435 },
436 unnecessary_code_fade: Default::default(),
437 }
438 }
439}
440
441pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
442 let show_background = language_settings::language_settings(None, None, cx)
443 .inlay_hints
444 .show_background;
445
446 HighlightStyle {
447 color: Some(cx.theme().status().hint),
448 background_color: show_background.then(|| cx.theme().status().hint_background),
449 ..HighlightStyle::default()
450 }
451}
452
453pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
454 InlineCompletionStyles {
455 insertion: HighlightStyle {
456 color: Some(cx.theme().status().predictive),
457 ..HighlightStyle::default()
458 },
459 whitespace: HighlightStyle {
460 background_color: Some(cx.theme().status().created_background),
461 ..HighlightStyle::default()
462 },
463 }
464}
465
466type CompletionId = usize;
467
468pub(crate) enum EditDisplayMode {
469 TabAccept(bool),
470 DiffPopover,
471 Inline,
472}
473
474enum InlineCompletion {
475 Edit {
476 edits: Vec<(Range<Anchor>, String)>,
477 edit_preview: Option<EditPreview>,
478 display_mode: EditDisplayMode,
479 snapshot: BufferSnapshot,
480 },
481 Move {
482 target: Anchor,
483 range_around_target: Range<text::Anchor>,
484 snapshot: BufferSnapshot,
485 },
486}
487
488struct InlineCompletionState {
489 inlay_ids: Vec<InlayId>,
490 completion: InlineCompletion,
491 invalidation_range: Range<Anchor>,
492}
493
494impl InlineCompletionState {
495 pub fn is_move(&self) -> bool {
496 match &self.completion {
497 InlineCompletion::Move { .. } => true,
498 _ => false,
499 }
500 }
501}
502
503enum InlineCompletionHighlight {}
504
505pub enum MenuInlineCompletionsPolicy {
506 Never,
507 ByProvider,
508}
509
510#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
511struct EditorActionId(usize);
512
513impl EditorActionId {
514 pub fn post_inc(&mut self) -> Self {
515 let answer = self.0;
516
517 *self = Self(answer + 1);
518
519 Self(answer)
520 }
521}
522
523// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
524// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
525
526type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
527type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
528
529#[derive(Default)]
530struct ScrollbarMarkerState {
531 scrollbar_size: Size<Pixels>,
532 dirty: bool,
533 markers: Arc<[PaintQuad]>,
534 pending_refresh: Option<Task<Result<()>>>,
535}
536
537impl ScrollbarMarkerState {
538 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
539 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
540 }
541}
542
543#[derive(Clone, Debug)]
544struct RunnableTasks {
545 templates: Vec<(TaskSourceKind, TaskTemplate)>,
546 offset: MultiBufferOffset,
547 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
548 column: u32,
549 // Values of all named captures, including those starting with '_'
550 extra_variables: HashMap<String, String>,
551 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
552 context_range: Range<BufferOffset>,
553}
554
555impl RunnableTasks {
556 fn resolve<'a>(
557 &'a self,
558 cx: &'a task::TaskContext,
559 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
560 self.templates.iter().filter_map(|(kind, template)| {
561 template
562 .resolve_task(&kind.to_id_base(), cx)
563 .map(|task| (kind.clone(), task))
564 })
565 }
566}
567
568#[derive(Clone)]
569struct ResolvedTasks {
570 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
571 position: Anchor,
572}
573#[derive(Copy, Clone, Debug)]
574struct MultiBufferOffset(usize);
575#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
576struct BufferOffset(usize);
577
578// Addons allow storing per-editor state in other crates (e.g. Vim)
579pub trait Addon: 'static {
580 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
581
582 fn to_any(&self) -> &dyn std::any::Any;
583}
584
585#[derive(Debug, Copy, Clone, PartialEq, Eq)]
586pub enum IsVimMode {
587 Yes,
588 No,
589}
590
591/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
592///
593/// See the [module level documentation](self) for more information.
594pub struct Editor {
595 focus_handle: FocusHandle,
596 last_focused_descendant: Option<WeakFocusHandle>,
597 /// The text buffer being edited
598 buffer: Entity<MultiBuffer>,
599 /// Map of how text in the buffer should be displayed.
600 /// Handles soft wraps, folds, fake inlay text insertions, etc.
601 pub display_map: Entity<DisplayMap>,
602 pub selections: SelectionsCollection,
603 pub scroll_manager: ScrollManager,
604 /// When inline assist editors are linked, they all render cursors because
605 /// typing enters text into each of them, even the ones that aren't focused.
606 pub(crate) show_cursor_when_unfocused: bool,
607 columnar_selection_tail: Option<Anchor>,
608 add_selections_state: Option<AddSelectionsState>,
609 select_next_state: Option<SelectNextState>,
610 select_prev_state: Option<SelectNextState>,
611 selection_history: SelectionHistory,
612 autoclose_regions: Vec<AutocloseRegion>,
613 snippet_stack: InvalidationStack<SnippetState>,
614 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
615 ime_transaction: Option<TransactionId>,
616 active_diagnostics: Option<ActiveDiagnosticGroup>,
617 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
618
619 // TODO: make this a access method
620 pub project: Option<Entity<Project>>,
621 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
622 completion_provider: Option<Box<dyn CompletionProvider>>,
623 collaboration_hub: Option<Box<dyn CollaborationHub>>,
624 blink_manager: Entity<BlinkManager>,
625 show_cursor_names: bool,
626 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
627 pub show_local_selections: bool,
628 mode: EditorMode,
629 show_breadcrumbs: bool,
630 show_gutter: bool,
631 show_scrollbars: bool,
632 show_line_numbers: Option<bool>,
633 use_relative_line_numbers: Option<bool>,
634 show_git_diff_gutter: Option<bool>,
635 show_code_actions: Option<bool>,
636 show_runnables: Option<bool>,
637 show_wrap_guides: Option<bool>,
638 show_indent_guides: Option<bool>,
639 placeholder_text: Option<Arc<str>>,
640 highlight_order: usize,
641 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
642 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
643 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
644 scrollbar_marker_state: ScrollbarMarkerState,
645 active_indent_guides_state: ActiveIndentGuidesState,
646 nav_history: Option<ItemNavHistory>,
647 context_menu: RefCell<Option<CodeContextMenu>>,
648 mouse_context_menu: Option<MouseContextMenu>,
649 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
650 signature_help_state: SignatureHelpState,
651 auto_signature_help: Option<bool>,
652 find_all_references_task_sources: Vec<Anchor>,
653 next_completion_id: CompletionId,
654 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
655 code_actions_task: Option<Task<Result<()>>>,
656 document_highlights_task: Option<Task<()>>,
657 linked_editing_range_task: Option<Task<Option<()>>>,
658 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
659 pending_rename: Option<RenameState>,
660 searchable: bool,
661 cursor_shape: CursorShape,
662 current_line_highlight: Option<CurrentLineHighlight>,
663 collapse_matches: bool,
664 autoindent_mode: Option<AutoindentMode>,
665 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
666 input_enabled: bool,
667 use_modal_editing: bool,
668 read_only: bool,
669 leader_peer_id: Option<PeerId>,
670 remote_id: Option<ViewId>,
671 hover_state: HoverState,
672 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
673 gutter_hovered: bool,
674 hovered_link_state: Option<HoveredLinkState>,
675 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
676 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
677 active_inline_completion: Option<InlineCompletionState>,
678 /// Used to prevent flickering as the user types while the menu is open
679 stale_inline_completion_in_menu: Option<InlineCompletionState>,
680 // enable_inline_completions is a switch that Vim can use to disable
681 // edit predictions based on its mode.
682 enable_inline_completions: bool,
683 show_inline_completions_override: Option<bool>,
684 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
685 inlay_hint_cache: InlayHintCache,
686 next_inlay_id: usize,
687 _subscriptions: Vec<Subscription>,
688 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
689 gutter_dimensions: GutterDimensions,
690 style: Option<EditorStyle>,
691 text_style_refinement: Option<TextStyleRefinement>,
692 next_editor_action_id: EditorActionId,
693 editor_actions:
694 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
695 use_autoclose: bool,
696 use_auto_surround: bool,
697 auto_replace_emoji_shortcode: bool,
698 show_git_blame_gutter: bool,
699 show_git_blame_inline: bool,
700 show_git_blame_inline_delay_task: Option<Task<()>>,
701 git_blame_inline_enabled: bool,
702 serialize_dirty_buffers: bool,
703 show_selection_menu: Option<bool>,
704 blame: Option<Entity<GitBlame>>,
705 blame_subscription: Option<Subscription>,
706 custom_context_menu: Option<
707 Box<
708 dyn 'static
709 + Fn(
710 &mut Self,
711 DisplayPoint,
712 &mut Window,
713 &mut Context<Self>,
714 ) -> Option<Entity<ui::ContextMenu>>,
715 >,
716 >,
717 last_bounds: Option<Bounds<Pixels>>,
718 expect_bounds_change: Option<Bounds<Pixels>>,
719 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
720 tasks_update_task: Option<Task<()>>,
721 in_project_search: bool,
722 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
723 breadcrumb_header: Option<String>,
724 focused_block: Option<FocusedBlock>,
725 next_scroll_position: NextScrollCursorCenterTopBottom,
726 addons: HashMap<TypeId, Box<dyn Addon>>,
727 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
728 selection_mark_mode: bool,
729 toggle_fold_multiple_buffers: Task<()>,
730 _scroll_cursor_center_top_bottom_task: Task<()>,
731}
732
733#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
734enum NextScrollCursorCenterTopBottom {
735 #[default]
736 Center,
737 Top,
738 Bottom,
739}
740
741impl NextScrollCursorCenterTopBottom {
742 fn next(&self) -> Self {
743 match self {
744 Self::Center => Self::Top,
745 Self::Top => Self::Bottom,
746 Self::Bottom => Self::Center,
747 }
748 }
749}
750
751#[derive(Clone)]
752pub struct EditorSnapshot {
753 pub mode: EditorMode,
754 show_gutter: bool,
755 show_line_numbers: Option<bool>,
756 show_git_diff_gutter: Option<bool>,
757 show_code_actions: Option<bool>,
758 show_runnables: Option<bool>,
759 git_blame_gutter_max_author_length: Option<usize>,
760 pub display_snapshot: DisplaySnapshot,
761 pub placeholder_text: Option<Arc<str>>,
762 is_focused: bool,
763 scroll_anchor: ScrollAnchor,
764 ongoing_scroll: OngoingScroll,
765 current_line_highlight: CurrentLineHighlight,
766 gutter_hovered: bool,
767}
768
769const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
770
771#[derive(Default, Debug, Clone, Copy)]
772pub struct GutterDimensions {
773 pub left_padding: Pixels,
774 pub right_padding: Pixels,
775 pub width: Pixels,
776 pub margin: Pixels,
777 pub git_blame_entries_width: Option<Pixels>,
778}
779
780impl GutterDimensions {
781 /// The full width of the space taken up by the gutter.
782 pub fn full_width(&self) -> Pixels {
783 self.margin + self.width
784 }
785
786 /// The width of the space reserved for the fold indicators,
787 /// use alongside 'justify_end' and `gutter_width` to
788 /// right align content with the line numbers
789 pub fn fold_area_width(&self) -> Pixels {
790 self.margin + self.right_padding
791 }
792}
793
794#[derive(Debug)]
795pub struct RemoteSelection {
796 pub replica_id: ReplicaId,
797 pub selection: Selection<Anchor>,
798 pub cursor_shape: CursorShape,
799 pub peer_id: PeerId,
800 pub line_mode: bool,
801 pub participant_index: Option<ParticipantIndex>,
802 pub user_name: Option<SharedString>,
803}
804
805#[derive(Clone, Debug)]
806struct SelectionHistoryEntry {
807 selections: Arc<[Selection<Anchor>]>,
808 select_next_state: Option<SelectNextState>,
809 select_prev_state: Option<SelectNextState>,
810 add_selections_state: Option<AddSelectionsState>,
811}
812
813enum SelectionHistoryMode {
814 Normal,
815 Undoing,
816 Redoing,
817}
818
819#[derive(Clone, PartialEq, Eq, Hash)]
820struct HoveredCursor {
821 replica_id: u16,
822 selection_id: usize,
823}
824
825impl Default for SelectionHistoryMode {
826 fn default() -> Self {
827 Self::Normal
828 }
829}
830
831#[derive(Default)]
832struct SelectionHistory {
833 #[allow(clippy::type_complexity)]
834 selections_by_transaction:
835 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
836 mode: SelectionHistoryMode,
837 undo_stack: VecDeque<SelectionHistoryEntry>,
838 redo_stack: VecDeque<SelectionHistoryEntry>,
839}
840
841impl SelectionHistory {
842 fn insert_transaction(
843 &mut self,
844 transaction_id: TransactionId,
845 selections: Arc<[Selection<Anchor>]>,
846 ) {
847 self.selections_by_transaction
848 .insert(transaction_id, (selections, None));
849 }
850
851 #[allow(clippy::type_complexity)]
852 fn transaction(
853 &self,
854 transaction_id: TransactionId,
855 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
856 self.selections_by_transaction.get(&transaction_id)
857 }
858
859 #[allow(clippy::type_complexity)]
860 fn transaction_mut(
861 &mut self,
862 transaction_id: TransactionId,
863 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
864 self.selections_by_transaction.get_mut(&transaction_id)
865 }
866
867 fn push(&mut self, entry: SelectionHistoryEntry) {
868 if !entry.selections.is_empty() {
869 match self.mode {
870 SelectionHistoryMode::Normal => {
871 self.push_undo(entry);
872 self.redo_stack.clear();
873 }
874 SelectionHistoryMode::Undoing => self.push_redo(entry),
875 SelectionHistoryMode::Redoing => self.push_undo(entry),
876 }
877 }
878 }
879
880 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
881 if self
882 .undo_stack
883 .back()
884 .map_or(true, |e| e.selections != entry.selections)
885 {
886 self.undo_stack.push_back(entry);
887 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
888 self.undo_stack.pop_front();
889 }
890 }
891 }
892
893 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
894 if self
895 .redo_stack
896 .back()
897 .map_or(true, |e| e.selections != entry.selections)
898 {
899 self.redo_stack.push_back(entry);
900 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
901 self.redo_stack.pop_front();
902 }
903 }
904 }
905}
906
907struct RowHighlight {
908 index: usize,
909 range: Range<Anchor>,
910 color: Hsla,
911 should_autoscroll: bool,
912}
913
914#[derive(Clone, Debug)]
915struct AddSelectionsState {
916 above: bool,
917 stack: Vec<usize>,
918}
919
920#[derive(Clone)]
921struct SelectNextState {
922 query: AhoCorasick,
923 wordwise: bool,
924 done: bool,
925}
926
927impl std::fmt::Debug for SelectNextState {
928 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
929 f.debug_struct(std::any::type_name::<Self>())
930 .field("wordwise", &self.wordwise)
931 .field("done", &self.done)
932 .finish()
933 }
934}
935
936#[derive(Debug)]
937struct AutocloseRegion {
938 selection_id: usize,
939 range: Range<Anchor>,
940 pair: BracketPair,
941}
942
943#[derive(Debug)]
944struct SnippetState {
945 ranges: Vec<Vec<Range<Anchor>>>,
946 active_index: usize,
947 choices: Vec<Option<Vec<String>>>,
948}
949
950#[doc(hidden)]
951pub struct RenameState {
952 pub range: Range<Anchor>,
953 pub old_name: Arc<str>,
954 pub editor: Entity<Editor>,
955 block_id: CustomBlockId,
956}
957
958struct InvalidationStack<T>(Vec<T>);
959
960struct RegisteredInlineCompletionProvider {
961 provider: Arc<dyn InlineCompletionProviderHandle>,
962 _subscription: Subscription,
963}
964
965#[derive(Debug)]
966struct ActiveDiagnosticGroup {
967 primary_range: Range<Anchor>,
968 primary_message: String,
969 group_id: usize,
970 blocks: HashMap<CustomBlockId, Diagnostic>,
971 is_valid: bool,
972}
973
974#[derive(Serialize, Deserialize, Clone, Debug)]
975pub struct ClipboardSelection {
976 pub len: usize,
977 pub is_entire_line: bool,
978 pub first_line_indent: u32,
979}
980
981#[derive(Debug)]
982pub(crate) struct NavigationData {
983 cursor_anchor: Anchor,
984 cursor_position: Point,
985 scroll_anchor: ScrollAnchor,
986 scroll_top_row: u32,
987}
988
989#[derive(Debug, Clone, Copy, PartialEq, Eq)]
990pub enum GotoDefinitionKind {
991 Symbol,
992 Declaration,
993 Type,
994 Implementation,
995}
996
997#[derive(Debug, Clone)]
998enum InlayHintRefreshReason {
999 Toggle(bool),
1000 SettingsChange(InlayHintSettings),
1001 NewLinesShown,
1002 BufferEdited(HashSet<Arc<Language>>),
1003 RefreshRequested,
1004 ExcerptsRemoved(Vec<ExcerptId>),
1005}
1006
1007impl InlayHintRefreshReason {
1008 fn description(&self) -> &'static str {
1009 match self {
1010 Self::Toggle(_) => "toggle",
1011 Self::SettingsChange(_) => "settings change",
1012 Self::NewLinesShown => "new lines shown",
1013 Self::BufferEdited(_) => "buffer edited",
1014 Self::RefreshRequested => "refresh requested",
1015 Self::ExcerptsRemoved(_) => "excerpts removed",
1016 }
1017 }
1018}
1019
1020pub enum FormatTarget {
1021 Buffers,
1022 Ranges(Vec<Range<MultiBufferPoint>>),
1023}
1024
1025pub(crate) struct FocusedBlock {
1026 id: BlockId,
1027 focus_handle: WeakFocusHandle,
1028}
1029
1030#[derive(Clone)]
1031enum JumpData {
1032 MultiBufferRow {
1033 row: MultiBufferRow,
1034 line_offset_from_top: u32,
1035 },
1036 MultiBufferPoint {
1037 excerpt_id: ExcerptId,
1038 position: Point,
1039 anchor: text::Anchor,
1040 line_offset_from_top: u32,
1041 },
1042}
1043
1044pub enum MultibufferSelectionMode {
1045 First,
1046 All,
1047}
1048
1049impl Editor {
1050 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1051 let buffer = cx.new(|cx| Buffer::local("", cx));
1052 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1053 Self::new(
1054 EditorMode::SingleLine { auto_width: false },
1055 buffer,
1056 None,
1057 false,
1058 window,
1059 cx,
1060 )
1061 }
1062
1063 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1064 let buffer = cx.new(|cx| Buffer::local("", cx));
1065 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1066 Self::new(EditorMode::Full, buffer, None, false, window, cx)
1067 }
1068
1069 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1070 let buffer = cx.new(|cx| Buffer::local("", cx));
1071 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1072 Self::new(
1073 EditorMode::SingleLine { auto_width: true },
1074 buffer,
1075 None,
1076 false,
1077 window,
1078 cx,
1079 )
1080 }
1081
1082 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1083 let buffer = cx.new(|cx| Buffer::local("", cx));
1084 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1085 Self::new(
1086 EditorMode::AutoHeight { max_lines },
1087 buffer,
1088 None,
1089 false,
1090 window,
1091 cx,
1092 )
1093 }
1094
1095 pub fn for_buffer(
1096 buffer: Entity<Buffer>,
1097 project: Option<Entity<Project>>,
1098 window: &mut Window,
1099 cx: &mut Context<Self>,
1100 ) -> Self {
1101 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1102 Self::new(EditorMode::Full, buffer, project, false, window, cx)
1103 }
1104
1105 pub fn for_multibuffer(
1106 buffer: Entity<MultiBuffer>,
1107 project: Option<Entity<Project>>,
1108 show_excerpt_controls: bool,
1109 window: &mut Window,
1110 cx: &mut Context<Self>,
1111 ) -> Self {
1112 Self::new(
1113 EditorMode::Full,
1114 buffer,
1115 project,
1116 show_excerpt_controls,
1117 window,
1118 cx,
1119 )
1120 }
1121
1122 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1123 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1124 let mut clone = Self::new(
1125 self.mode,
1126 self.buffer.clone(),
1127 self.project.clone(),
1128 show_excerpt_controls,
1129 window,
1130 cx,
1131 );
1132 self.display_map.update(cx, |display_map, cx| {
1133 let snapshot = display_map.snapshot(cx);
1134 clone.display_map.update(cx, |display_map, cx| {
1135 display_map.set_state(&snapshot, cx);
1136 });
1137 });
1138 clone.selections.clone_state(&self.selections);
1139 clone.scroll_manager.clone_state(&self.scroll_manager);
1140 clone.searchable = self.searchable;
1141 clone
1142 }
1143
1144 pub fn new(
1145 mode: EditorMode,
1146 buffer: Entity<MultiBuffer>,
1147 project: Option<Entity<Project>>,
1148 show_excerpt_controls: bool,
1149 window: &mut Window,
1150 cx: &mut Context<Self>,
1151 ) -> Self {
1152 let style = window.text_style();
1153 let font_size = style.font_size.to_pixels(window.rem_size());
1154 let editor = cx.entity().downgrade();
1155 let fold_placeholder = FoldPlaceholder {
1156 constrain_width: true,
1157 render: Arc::new(move |fold_id, fold_range, _, cx| {
1158 let editor = editor.clone();
1159 div()
1160 .id(fold_id)
1161 .bg(cx.theme().colors().ghost_element_background)
1162 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1163 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1164 .rounded_sm()
1165 .size_full()
1166 .cursor_pointer()
1167 .child("⋯")
1168 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1169 .on_click(move |_, _window, cx| {
1170 editor
1171 .update(cx, |editor, cx| {
1172 editor.unfold_ranges(
1173 &[fold_range.start..fold_range.end],
1174 true,
1175 false,
1176 cx,
1177 );
1178 cx.stop_propagation();
1179 })
1180 .ok();
1181 })
1182 .into_any()
1183 }),
1184 merge_adjacent: true,
1185 ..Default::default()
1186 };
1187 let display_map = cx.new(|cx| {
1188 DisplayMap::new(
1189 buffer.clone(),
1190 style.font(),
1191 font_size,
1192 None,
1193 show_excerpt_controls,
1194 FILE_HEADER_HEIGHT,
1195 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1196 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1197 fold_placeholder,
1198 cx,
1199 )
1200 });
1201
1202 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1203
1204 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1205
1206 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1207 .then(|| language_settings::SoftWrap::None);
1208
1209 let mut project_subscriptions = Vec::new();
1210 if mode == EditorMode::Full {
1211 if let Some(project) = project.as_ref() {
1212 if buffer.read(cx).is_singleton() {
1213 project_subscriptions.push(cx.observe_in(project, window, |_, _, _, cx| {
1214 cx.emit(EditorEvent::TitleChanged);
1215 }));
1216 }
1217 project_subscriptions.push(cx.subscribe_in(
1218 project,
1219 window,
1220 |editor, _, event, window, cx| {
1221 if let project::Event::RefreshInlayHints = event {
1222 editor
1223 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1224 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1225 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1226 let focus_handle = editor.focus_handle(cx);
1227 if focus_handle.is_focused(window) {
1228 let snapshot = buffer.read(cx).snapshot();
1229 for (range, snippet) in snippet_edits {
1230 let editor_range =
1231 language::range_from_lsp(*range).to_offset(&snapshot);
1232 editor
1233 .insert_snippet(
1234 &[editor_range],
1235 snippet.clone(),
1236 window,
1237 cx,
1238 )
1239 .ok();
1240 }
1241 }
1242 }
1243 }
1244 },
1245 ));
1246 if let Some(task_inventory) = project
1247 .read(cx)
1248 .task_store()
1249 .read(cx)
1250 .task_inventory()
1251 .cloned()
1252 {
1253 project_subscriptions.push(cx.observe_in(
1254 &task_inventory,
1255 window,
1256 |editor, _, window, cx| {
1257 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1258 },
1259 ));
1260 }
1261 }
1262 }
1263
1264 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1265
1266 let inlay_hint_settings =
1267 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1268 let focus_handle = cx.focus_handle();
1269 cx.on_focus(&focus_handle, window, Self::handle_focus)
1270 .detach();
1271 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1272 .detach();
1273 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1274 .detach();
1275 cx.on_blur(&focus_handle, window, Self::handle_blur)
1276 .detach();
1277
1278 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1279 Some(false)
1280 } else {
1281 None
1282 };
1283
1284 let mut code_action_providers = Vec::new();
1285 if let Some(project) = project.clone() {
1286 get_unstaged_changes_for_buffers(
1287 &project,
1288 buffer.read(cx).all_buffers(),
1289 buffer.clone(),
1290 cx,
1291 );
1292 code_action_providers.push(Rc::new(project) as Rc<_>);
1293 }
1294
1295 let mut this = Self {
1296 focus_handle,
1297 show_cursor_when_unfocused: false,
1298 last_focused_descendant: None,
1299 buffer: buffer.clone(),
1300 display_map: display_map.clone(),
1301 selections,
1302 scroll_manager: ScrollManager::new(cx),
1303 columnar_selection_tail: None,
1304 add_selections_state: None,
1305 select_next_state: None,
1306 select_prev_state: None,
1307 selection_history: Default::default(),
1308 autoclose_regions: Default::default(),
1309 snippet_stack: Default::default(),
1310 select_larger_syntax_node_stack: Vec::new(),
1311 ime_transaction: Default::default(),
1312 active_diagnostics: None,
1313 soft_wrap_mode_override,
1314 completion_provider: project.clone().map(|project| Box::new(project) as _),
1315 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1316 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1317 project,
1318 blink_manager: blink_manager.clone(),
1319 show_local_selections: true,
1320 show_scrollbars: true,
1321 mode,
1322 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1323 show_gutter: mode == EditorMode::Full,
1324 show_line_numbers: None,
1325 use_relative_line_numbers: None,
1326 show_git_diff_gutter: None,
1327 show_code_actions: None,
1328 show_runnables: None,
1329 show_wrap_guides: None,
1330 show_indent_guides,
1331 placeholder_text: None,
1332 highlight_order: 0,
1333 highlighted_rows: HashMap::default(),
1334 background_highlights: Default::default(),
1335 gutter_highlights: TreeMap::default(),
1336 scrollbar_marker_state: ScrollbarMarkerState::default(),
1337 active_indent_guides_state: ActiveIndentGuidesState::default(),
1338 nav_history: None,
1339 context_menu: RefCell::new(None),
1340 mouse_context_menu: None,
1341 completion_tasks: Default::default(),
1342 signature_help_state: SignatureHelpState::default(),
1343 auto_signature_help: None,
1344 find_all_references_task_sources: Vec::new(),
1345 next_completion_id: 0,
1346 next_inlay_id: 0,
1347 code_action_providers,
1348 available_code_actions: Default::default(),
1349 code_actions_task: Default::default(),
1350 document_highlights_task: Default::default(),
1351 linked_editing_range_task: Default::default(),
1352 pending_rename: Default::default(),
1353 searchable: true,
1354 cursor_shape: EditorSettings::get_global(cx)
1355 .cursor_shape
1356 .unwrap_or_default(),
1357 current_line_highlight: None,
1358 autoindent_mode: Some(AutoindentMode::EachLine),
1359 collapse_matches: false,
1360 workspace: None,
1361 input_enabled: true,
1362 use_modal_editing: mode == EditorMode::Full,
1363 read_only: false,
1364 use_autoclose: true,
1365 use_auto_surround: true,
1366 auto_replace_emoji_shortcode: false,
1367 leader_peer_id: None,
1368 remote_id: None,
1369 hover_state: Default::default(),
1370 pending_mouse_down: None,
1371 hovered_link_state: Default::default(),
1372 inline_completion_provider: None,
1373 active_inline_completion: None,
1374 stale_inline_completion_in_menu: None,
1375 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1376
1377 gutter_hovered: false,
1378 pixel_position_of_newest_cursor: None,
1379 last_bounds: None,
1380 expect_bounds_change: None,
1381 gutter_dimensions: GutterDimensions::default(),
1382 style: None,
1383 show_cursor_names: false,
1384 hovered_cursors: Default::default(),
1385 next_editor_action_id: EditorActionId::default(),
1386 editor_actions: Rc::default(),
1387 show_inline_completions_override: None,
1388 enable_inline_completions: true,
1389 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1390 custom_context_menu: None,
1391 show_git_blame_gutter: false,
1392 show_git_blame_inline: false,
1393 show_selection_menu: None,
1394 show_git_blame_inline_delay_task: None,
1395 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1396 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1397 .session
1398 .restore_unsaved_buffers,
1399 blame: None,
1400 blame_subscription: None,
1401 tasks: Default::default(),
1402 _subscriptions: vec![
1403 cx.observe(&buffer, Self::on_buffer_changed),
1404 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1405 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1406 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1407 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1408 cx.observe_window_activation(window, |editor, window, cx| {
1409 let active = window.is_window_active();
1410 editor.blink_manager.update(cx, |blink_manager, cx| {
1411 if active {
1412 blink_manager.enable(cx);
1413 } else {
1414 blink_manager.disable(cx);
1415 }
1416 });
1417 }),
1418 ],
1419 tasks_update_task: None,
1420 linked_edit_ranges: Default::default(),
1421 in_project_search: false,
1422 previous_search_ranges: None,
1423 breadcrumb_header: None,
1424 focused_block: None,
1425 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1426 addons: HashMap::default(),
1427 registered_buffers: HashMap::default(),
1428 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1429 selection_mark_mode: false,
1430 toggle_fold_multiple_buffers: Task::ready(()),
1431 text_style_refinement: None,
1432 };
1433 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1434 this._subscriptions.extend(project_subscriptions);
1435
1436 this.end_selection(window, cx);
1437 this.scroll_manager.show_scrollbar(window, cx);
1438
1439 if mode == EditorMode::Full {
1440 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1441 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1442
1443 if this.git_blame_inline_enabled {
1444 this.git_blame_inline_enabled = true;
1445 this.start_git_blame_inline(false, window, cx);
1446 }
1447
1448 if let Some(buffer) = buffer.read(cx).as_singleton() {
1449 if let Some(project) = this.project.as_ref() {
1450 let lsp_store = project.read(cx).lsp_store();
1451 let handle = lsp_store.update(cx, |lsp_store, cx| {
1452 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1453 });
1454 this.registered_buffers
1455 .insert(buffer.read(cx).remote_id(), handle);
1456 }
1457 }
1458 }
1459
1460 this.report_editor_event("Editor Opened", None, cx);
1461 this
1462 }
1463
1464 pub fn mouse_menu_is_focused(&self, window: &mut Window, cx: &mut App) -> bool {
1465 self.mouse_context_menu
1466 .as_ref()
1467 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1468 }
1469
1470 fn key_context(&self, window: &mut Window, cx: &mut Context<Self>) -> KeyContext {
1471 let mut key_context = KeyContext::new_with_defaults();
1472 key_context.add("Editor");
1473 let mode = match self.mode {
1474 EditorMode::SingleLine { .. } => "single_line",
1475 EditorMode::AutoHeight { .. } => "auto_height",
1476 EditorMode::Full => "full",
1477 };
1478
1479 if EditorSettings::jupyter_enabled(cx) {
1480 key_context.add("jupyter");
1481 }
1482
1483 key_context.set("mode", mode);
1484 if self.pending_rename.is_some() {
1485 key_context.add("renaming");
1486 }
1487 match self.context_menu.borrow().as_ref() {
1488 Some(CodeContextMenu::Completions(_)) => {
1489 key_context.add("menu");
1490 key_context.add("showing_completions");
1491 }
1492 Some(CodeContextMenu::CodeActions(_)) => {
1493 key_context.add("menu");
1494 key_context.add("showing_code_actions")
1495 }
1496 None => {}
1497 }
1498
1499 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1500 if !self.focus_handle(cx).contains_focused(window, cx)
1501 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1502 {
1503 for addon in self.addons.values() {
1504 addon.extend_key_context(&mut key_context, cx)
1505 }
1506 }
1507
1508 if let Some(extension) = self
1509 .buffer
1510 .read(cx)
1511 .as_singleton()
1512 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1513 {
1514 key_context.set("extension", extension.to_string());
1515 }
1516
1517 if self.has_active_inline_completion() {
1518 key_context.add("copilot_suggestion");
1519 key_context.add("inline_completion");
1520 }
1521
1522 if self.selection_mark_mode {
1523 key_context.add("selection_mode");
1524 }
1525
1526 key_context
1527 }
1528
1529 pub fn new_file(
1530 workspace: &mut Workspace,
1531 _: &workspace::NewFile,
1532 window: &mut Window,
1533 cx: &mut Context<Workspace>,
1534 ) {
1535 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1536 "Failed to create buffer",
1537 window,
1538 cx,
1539 |e, _, _| match e.error_code() {
1540 ErrorCode::RemoteUpgradeRequired => Some(format!(
1541 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1542 e.error_tag("required").unwrap_or("the latest version")
1543 )),
1544 _ => None,
1545 },
1546 );
1547 }
1548
1549 pub fn new_in_workspace(
1550 workspace: &mut Workspace,
1551 window: &mut Window,
1552 cx: &mut Context<Workspace>,
1553 ) -> Task<Result<Entity<Editor>>> {
1554 let project = workspace.project().clone();
1555 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1556
1557 cx.spawn_in(window, |workspace, mut cx| async move {
1558 let buffer = create.await?;
1559 workspace.update_in(&mut cx, |workspace, window, cx| {
1560 let editor =
1561 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1562 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1563 editor
1564 })
1565 })
1566 }
1567
1568 fn new_file_vertical(
1569 workspace: &mut Workspace,
1570 _: &workspace::NewFileSplitVertical,
1571 window: &mut Window,
1572 cx: &mut Context<Workspace>,
1573 ) {
1574 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1575 }
1576
1577 fn new_file_horizontal(
1578 workspace: &mut Workspace,
1579 _: &workspace::NewFileSplitHorizontal,
1580 window: &mut Window,
1581 cx: &mut Context<Workspace>,
1582 ) {
1583 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1584 }
1585
1586 fn new_file_in_direction(
1587 workspace: &mut Workspace,
1588 direction: SplitDirection,
1589 window: &mut Window,
1590 cx: &mut Context<Workspace>,
1591 ) {
1592 let project = workspace.project().clone();
1593 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1594
1595 cx.spawn_in(window, |workspace, mut cx| async move {
1596 let buffer = create.await?;
1597 workspace.update_in(&mut cx, move |workspace, window, cx| {
1598 workspace.split_item(
1599 direction,
1600 Box::new(
1601 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1602 ),
1603 window,
1604 cx,
1605 )
1606 })?;
1607 anyhow::Ok(())
1608 })
1609 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1610 match e.error_code() {
1611 ErrorCode::RemoteUpgradeRequired => Some(format!(
1612 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1613 e.error_tag("required").unwrap_or("the latest version")
1614 )),
1615 _ => None,
1616 }
1617 });
1618 }
1619
1620 pub fn leader_peer_id(&self) -> Option<PeerId> {
1621 self.leader_peer_id
1622 }
1623
1624 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1625 &self.buffer
1626 }
1627
1628 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1629 self.workspace.as_ref()?.0.upgrade()
1630 }
1631
1632 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1633 self.buffer().read(cx).title(cx)
1634 }
1635
1636 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1637 let git_blame_gutter_max_author_length = self
1638 .render_git_blame_gutter(cx)
1639 .then(|| {
1640 if let Some(blame) = self.blame.as_ref() {
1641 let max_author_length =
1642 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1643 Some(max_author_length)
1644 } else {
1645 None
1646 }
1647 })
1648 .flatten();
1649
1650 EditorSnapshot {
1651 mode: self.mode,
1652 show_gutter: self.show_gutter,
1653 show_line_numbers: self.show_line_numbers,
1654 show_git_diff_gutter: self.show_git_diff_gutter,
1655 show_code_actions: self.show_code_actions,
1656 show_runnables: self.show_runnables,
1657 git_blame_gutter_max_author_length,
1658 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1659 scroll_anchor: self.scroll_manager.anchor(),
1660 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1661 placeholder_text: self.placeholder_text.clone(),
1662 is_focused: self.focus_handle.is_focused(window),
1663 current_line_highlight: self
1664 .current_line_highlight
1665 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1666 gutter_hovered: self.gutter_hovered,
1667 }
1668 }
1669
1670 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1671 self.buffer.read(cx).language_at(point, cx)
1672 }
1673
1674 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1675 self.buffer.read(cx).read(cx).file_at(point).cloned()
1676 }
1677
1678 pub fn active_excerpt(
1679 &self,
1680 cx: &App,
1681 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1682 self.buffer
1683 .read(cx)
1684 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1685 }
1686
1687 pub fn mode(&self) -> EditorMode {
1688 self.mode
1689 }
1690
1691 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1692 self.collaboration_hub.as_deref()
1693 }
1694
1695 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1696 self.collaboration_hub = Some(hub);
1697 }
1698
1699 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1700 self.in_project_search = in_project_search;
1701 }
1702
1703 pub fn set_custom_context_menu(
1704 &mut self,
1705 f: impl 'static
1706 + Fn(
1707 &mut Self,
1708 DisplayPoint,
1709 &mut Window,
1710 &mut Context<Self>,
1711 ) -> Option<Entity<ui::ContextMenu>>,
1712 ) {
1713 self.custom_context_menu = Some(Box::new(f))
1714 }
1715
1716 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1717 self.completion_provider = provider;
1718 }
1719
1720 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1721 self.semantics_provider.clone()
1722 }
1723
1724 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1725 self.semantics_provider = provider;
1726 }
1727
1728 pub fn set_inline_completion_provider<T>(
1729 &mut self,
1730 provider: Option<Entity<T>>,
1731 window: &mut Window,
1732 cx: &mut Context<Self>,
1733 ) where
1734 T: InlineCompletionProvider,
1735 {
1736 self.inline_completion_provider =
1737 provider.map(|provider| RegisteredInlineCompletionProvider {
1738 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1739 if this.focus_handle.is_focused(window) {
1740 this.update_visible_inline_completion(window, cx);
1741 }
1742 }),
1743 provider: Arc::new(provider),
1744 });
1745 self.refresh_inline_completion(false, false, window, cx);
1746 }
1747
1748 pub fn placeholder_text(&self) -> Option<&str> {
1749 self.placeholder_text.as_deref()
1750 }
1751
1752 pub fn set_placeholder_text(
1753 &mut self,
1754 placeholder_text: impl Into<Arc<str>>,
1755 cx: &mut Context<Self>,
1756 ) {
1757 let placeholder_text = Some(placeholder_text.into());
1758 if self.placeholder_text != placeholder_text {
1759 self.placeholder_text = placeholder_text;
1760 cx.notify();
1761 }
1762 }
1763
1764 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1765 self.cursor_shape = cursor_shape;
1766
1767 // Disrupt blink for immediate user feedback that the cursor shape has changed
1768 self.blink_manager.update(cx, BlinkManager::show_cursor);
1769
1770 cx.notify();
1771 }
1772
1773 pub fn set_current_line_highlight(
1774 &mut self,
1775 current_line_highlight: Option<CurrentLineHighlight>,
1776 ) {
1777 self.current_line_highlight = current_line_highlight;
1778 }
1779
1780 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1781 self.collapse_matches = collapse_matches;
1782 }
1783
1784 pub fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1785 let buffers = self.buffer.read(cx).all_buffers();
1786 let Some(lsp_store) = self.lsp_store(cx) else {
1787 return;
1788 };
1789 lsp_store.update(cx, |lsp_store, cx| {
1790 for buffer in buffers {
1791 self.registered_buffers
1792 .entry(buffer.read(cx).remote_id())
1793 .or_insert_with(|| {
1794 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1795 });
1796 }
1797 })
1798 }
1799
1800 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1801 if self.collapse_matches {
1802 return range.start..range.start;
1803 }
1804 range.clone()
1805 }
1806
1807 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1808 if self.display_map.read(cx).clip_at_line_ends != clip {
1809 self.display_map
1810 .update(cx, |map, _| map.clip_at_line_ends = clip);
1811 }
1812 }
1813
1814 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1815 self.input_enabled = input_enabled;
1816 }
1817
1818 pub fn set_inline_completions_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
1819 self.enable_inline_completions = enabled;
1820 if !self.enable_inline_completions {
1821 self.take_active_inline_completion(cx);
1822 cx.notify();
1823 }
1824 }
1825
1826 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1827 self.menu_inline_completions_policy = value;
1828 }
1829
1830 pub fn set_autoindent(&mut self, autoindent: bool) {
1831 if autoindent {
1832 self.autoindent_mode = Some(AutoindentMode::EachLine);
1833 } else {
1834 self.autoindent_mode = None;
1835 }
1836 }
1837
1838 pub fn read_only(&self, cx: &App) -> bool {
1839 self.read_only || self.buffer.read(cx).read_only()
1840 }
1841
1842 pub fn set_read_only(&mut self, read_only: bool) {
1843 self.read_only = read_only;
1844 }
1845
1846 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1847 self.use_autoclose = autoclose;
1848 }
1849
1850 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1851 self.use_auto_surround = auto_surround;
1852 }
1853
1854 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1855 self.auto_replace_emoji_shortcode = auto_replace;
1856 }
1857
1858 pub fn toggle_inline_completions(
1859 &mut self,
1860 _: &ToggleInlineCompletions,
1861 window: &mut Window,
1862 cx: &mut Context<Self>,
1863 ) {
1864 if self.show_inline_completions_override.is_some() {
1865 self.set_show_inline_completions(None, window, cx);
1866 } else {
1867 let cursor = self.selections.newest_anchor().head();
1868 if let Some((buffer, cursor_buffer_position)) =
1869 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1870 {
1871 let show_inline_completions =
1872 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
1873 self.set_show_inline_completions(Some(show_inline_completions), window, cx);
1874 }
1875 }
1876 }
1877
1878 pub fn set_show_inline_completions(
1879 &mut self,
1880 show_inline_completions: Option<bool>,
1881 window: &mut Window,
1882 cx: &mut Context<Self>,
1883 ) {
1884 self.show_inline_completions_override = show_inline_completions;
1885 self.refresh_inline_completion(false, true, window, cx);
1886 }
1887
1888 pub fn inline_completions_enabled(&self, cx: &App) -> bool {
1889 let cursor = self.selections.newest_anchor().head();
1890 if let Some((buffer, buffer_position)) =
1891 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1892 {
1893 self.should_show_inline_completions(&buffer, buffer_position, cx)
1894 } else {
1895 false
1896 }
1897 }
1898
1899 fn should_show_inline_completions(
1900 &self,
1901 buffer: &Entity<Buffer>,
1902 buffer_position: language::Anchor,
1903 cx: &App,
1904 ) -> bool {
1905 if !self.snippet_stack.is_empty() {
1906 return false;
1907 }
1908
1909 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
1910 return false;
1911 }
1912
1913 if let Some(provider) = self.inline_completion_provider() {
1914 if let Some(show_inline_completions) = self.show_inline_completions_override {
1915 show_inline_completions
1916 } else {
1917 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
1918 }
1919 } else {
1920 false
1921 }
1922 }
1923
1924 fn inline_completions_disabled_in_scope(
1925 &self,
1926 buffer: &Entity<Buffer>,
1927 buffer_position: language::Anchor,
1928 cx: &App,
1929 ) -> bool {
1930 let snapshot = buffer.read(cx).snapshot();
1931 let settings = snapshot.settings_at(buffer_position, cx);
1932
1933 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1934 return false;
1935 };
1936
1937 scope.override_name().map_or(false, |scope_name| {
1938 settings
1939 .inline_completions_disabled_in
1940 .iter()
1941 .any(|s| s == scope_name)
1942 })
1943 }
1944
1945 pub fn set_use_modal_editing(&mut self, to: bool) {
1946 self.use_modal_editing = to;
1947 }
1948
1949 pub fn use_modal_editing(&self) -> bool {
1950 self.use_modal_editing
1951 }
1952
1953 fn selections_did_change(
1954 &mut self,
1955 local: bool,
1956 old_cursor_position: &Anchor,
1957 show_completions: bool,
1958 window: &mut Window,
1959 cx: &mut Context<Self>,
1960 ) {
1961 window.invalidate_character_coordinates();
1962
1963 // Copy selections to primary selection buffer
1964 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1965 if local {
1966 let selections = self.selections.all::<usize>(cx);
1967 let buffer_handle = self.buffer.read(cx).read(cx);
1968
1969 let mut text = String::new();
1970 for (index, selection) in selections.iter().enumerate() {
1971 let text_for_selection = buffer_handle
1972 .text_for_range(selection.start..selection.end)
1973 .collect::<String>();
1974
1975 text.push_str(&text_for_selection);
1976 if index != selections.len() - 1 {
1977 text.push('\n');
1978 }
1979 }
1980
1981 if !text.is_empty() {
1982 cx.write_to_primary(ClipboardItem::new_string(text));
1983 }
1984 }
1985
1986 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
1987 self.buffer.update(cx, |buffer, cx| {
1988 buffer.set_active_selections(
1989 &self.selections.disjoint_anchors(),
1990 self.selections.line_mode,
1991 self.cursor_shape,
1992 cx,
1993 )
1994 });
1995 }
1996 let display_map = self
1997 .display_map
1998 .update(cx, |display_map, cx| display_map.snapshot(cx));
1999 let buffer = &display_map.buffer_snapshot;
2000 self.add_selections_state = None;
2001 self.select_next_state = None;
2002 self.select_prev_state = None;
2003 self.select_larger_syntax_node_stack.clear();
2004 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2005 self.snippet_stack
2006 .invalidate(&self.selections.disjoint_anchors(), buffer);
2007 self.take_rename(false, window, cx);
2008
2009 let new_cursor_position = self.selections.newest_anchor().head();
2010
2011 self.push_to_nav_history(
2012 *old_cursor_position,
2013 Some(new_cursor_position.to_point(buffer)),
2014 cx,
2015 );
2016
2017 if local {
2018 let new_cursor_position = self.selections.newest_anchor().head();
2019 let mut context_menu = self.context_menu.borrow_mut();
2020 let completion_menu = match context_menu.as_ref() {
2021 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2022 _ => {
2023 *context_menu = None;
2024 None
2025 }
2026 };
2027
2028 if let Some(completion_menu) = completion_menu {
2029 let cursor_position = new_cursor_position.to_offset(buffer);
2030 let (word_range, kind) =
2031 buffer.surrounding_word(completion_menu.initial_position, true);
2032 if kind == Some(CharKind::Word)
2033 && word_range.to_inclusive().contains(&cursor_position)
2034 {
2035 let mut completion_menu = completion_menu.clone();
2036 drop(context_menu);
2037
2038 let query = Self::completion_query(buffer, cursor_position);
2039 cx.spawn(move |this, mut cx| async move {
2040 completion_menu
2041 .filter(query.as_deref(), cx.background_executor().clone())
2042 .await;
2043
2044 this.update(&mut cx, |this, cx| {
2045 let mut context_menu = this.context_menu.borrow_mut();
2046 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2047 else {
2048 return;
2049 };
2050
2051 if menu.id > completion_menu.id {
2052 return;
2053 }
2054
2055 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2056 drop(context_menu);
2057 cx.notify();
2058 })
2059 })
2060 .detach();
2061
2062 if show_completions {
2063 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2064 }
2065 } else {
2066 drop(context_menu);
2067 self.hide_context_menu(window, cx);
2068 }
2069 } else {
2070 drop(context_menu);
2071 }
2072
2073 hide_hover(self, cx);
2074
2075 if old_cursor_position.to_display_point(&display_map).row()
2076 != new_cursor_position.to_display_point(&display_map).row()
2077 {
2078 self.available_code_actions.take();
2079 }
2080 self.refresh_code_actions(window, cx);
2081 self.refresh_document_highlights(cx);
2082 refresh_matching_bracket_highlights(self, window, cx);
2083 self.update_visible_inline_completion(window, cx);
2084 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2085 if self.git_blame_inline_enabled {
2086 self.start_inline_blame_timer(window, cx);
2087 }
2088 }
2089
2090 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2091 cx.emit(EditorEvent::SelectionsChanged { local });
2092
2093 if self.selections.disjoint_anchors().len() == 1 {
2094 cx.emit(SearchEvent::ActiveMatchChanged)
2095 }
2096 cx.notify();
2097 }
2098
2099 pub fn change_selections<R>(
2100 &mut self,
2101 autoscroll: Option<Autoscroll>,
2102 window: &mut Window,
2103 cx: &mut Context<Self>,
2104 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2105 ) -> R {
2106 self.change_selections_inner(autoscroll, true, window, cx, change)
2107 }
2108
2109 pub fn change_selections_inner<R>(
2110 &mut self,
2111 autoscroll: Option<Autoscroll>,
2112 request_completions: bool,
2113 window: &mut Window,
2114 cx: &mut Context<Self>,
2115 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2116 ) -> R {
2117 let old_cursor_position = self.selections.newest_anchor().head();
2118 self.push_to_selection_history();
2119
2120 let (changed, result) = self.selections.change_with(cx, change);
2121
2122 if changed {
2123 if let Some(autoscroll) = autoscroll {
2124 self.request_autoscroll(autoscroll, cx);
2125 }
2126 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2127
2128 if self.should_open_signature_help_automatically(
2129 &old_cursor_position,
2130 self.signature_help_state.backspace_pressed(),
2131 cx,
2132 ) {
2133 self.show_signature_help(&ShowSignatureHelp, window, cx);
2134 }
2135 self.signature_help_state.set_backspace_pressed(false);
2136 }
2137
2138 result
2139 }
2140
2141 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2142 where
2143 I: IntoIterator<Item = (Range<S>, T)>,
2144 S: ToOffset,
2145 T: Into<Arc<str>>,
2146 {
2147 if self.read_only(cx) {
2148 return;
2149 }
2150
2151 self.buffer
2152 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2153 }
2154
2155 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2156 where
2157 I: IntoIterator<Item = (Range<S>, T)>,
2158 S: ToOffset,
2159 T: Into<Arc<str>>,
2160 {
2161 if self.read_only(cx) {
2162 return;
2163 }
2164
2165 self.buffer.update(cx, |buffer, cx| {
2166 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2167 });
2168 }
2169
2170 pub fn edit_with_block_indent<I, S, T>(
2171 &mut self,
2172 edits: I,
2173 original_indent_columns: Vec<u32>,
2174 cx: &mut Context<Self>,
2175 ) where
2176 I: IntoIterator<Item = (Range<S>, T)>,
2177 S: ToOffset,
2178 T: Into<Arc<str>>,
2179 {
2180 if self.read_only(cx) {
2181 return;
2182 }
2183
2184 self.buffer.update(cx, |buffer, cx| {
2185 buffer.edit(
2186 edits,
2187 Some(AutoindentMode::Block {
2188 original_indent_columns,
2189 }),
2190 cx,
2191 )
2192 });
2193 }
2194
2195 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2196 self.hide_context_menu(window, cx);
2197
2198 match phase {
2199 SelectPhase::Begin {
2200 position,
2201 add,
2202 click_count,
2203 } => self.begin_selection(position, add, click_count, window, cx),
2204 SelectPhase::BeginColumnar {
2205 position,
2206 goal_column,
2207 reset,
2208 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2209 SelectPhase::Extend {
2210 position,
2211 click_count,
2212 } => self.extend_selection(position, click_count, window, cx),
2213 SelectPhase::Update {
2214 position,
2215 goal_column,
2216 scroll_delta,
2217 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2218 SelectPhase::End => self.end_selection(window, cx),
2219 }
2220 }
2221
2222 fn extend_selection(
2223 &mut self,
2224 position: DisplayPoint,
2225 click_count: usize,
2226 window: &mut Window,
2227 cx: &mut Context<Self>,
2228 ) {
2229 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2230 let tail = self.selections.newest::<usize>(cx).tail();
2231 self.begin_selection(position, false, click_count, window, cx);
2232
2233 let position = position.to_offset(&display_map, Bias::Left);
2234 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2235
2236 let mut pending_selection = self
2237 .selections
2238 .pending_anchor()
2239 .expect("extend_selection not called with pending selection");
2240 if position >= tail {
2241 pending_selection.start = tail_anchor;
2242 } else {
2243 pending_selection.end = tail_anchor;
2244 pending_selection.reversed = true;
2245 }
2246
2247 let mut pending_mode = self.selections.pending_mode().unwrap();
2248 match &mut pending_mode {
2249 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2250 _ => {}
2251 }
2252
2253 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2254 s.set_pending(pending_selection, pending_mode)
2255 });
2256 }
2257
2258 fn begin_selection(
2259 &mut self,
2260 position: DisplayPoint,
2261 add: bool,
2262 click_count: usize,
2263 window: &mut Window,
2264 cx: &mut Context<Self>,
2265 ) {
2266 if !self.focus_handle.is_focused(window) {
2267 self.last_focused_descendant = None;
2268 window.focus(&self.focus_handle);
2269 }
2270
2271 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2272 let buffer = &display_map.buffer_snapshot;
2273 let newest_selection = self.selections.newest_anchor().clone();
2274 let position = display_map.clip_point(position, Bias::Left);
2275
2276 let start;
2277 let end;
2278 let mode;
2279 let mut auto_scroll;
2280 match click_count {
2281 1 => {
2282 start = buffer.anchor_before(position.to_point(&display_map));
2283 end = start;
2284 mode = SelectMode::Character;
2285 auto_scroll = true;
2286 }
2287 2 => {
2288 let range = movement::surrounding_word(&display_map, position);
2289 start = buffer.anchor_before(range.start.to_point(&display_map));
2290 end = buffer.anchor_before(range.end.to_point(&display_map));
2291 mode = SelectMode::Word(start..end);
2292 auto_scroll = true;
2293 }
2294 3 => {
2295 let position = display_map
2296 .clip_point(position, Bias::Left)
2297 .to_point(&display_map);
2298 let line_start = display_map.prev_line_boundary(position).0;
2299 let next_line_start = buffer.clip_point(
2300 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2301 Bias::Left,
2302 );
2303 start = buffer.anchor_before(line_start);
2304 end = buffer.anchor_before(next_line_start);
2305 mode = SelectMode::Line(start..end);
2306 auto_scroll = true;
2307 }
2308 _ => {
2309 start = buffer.anchor_before(0);
2310 end = buffer.anchor_before(buffer.len());
2311 mode = SelectMode::All;
2312 auto_scroll = false;
2313 }
2314 }
2315 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2316
2317 let point_to_delete: Option<usize> = {
2318 let selected_points: Vec<Selection<Point>> =
2319 self.selections.disjoint_in_range(start..end, cx);
2320
2321 if !add || click_count > 1 {
2322 None
2323 } else if !selected_points.is_empty() {
2324 Some(selected_points[0].id)
2325 } else {
2326 let clicked_point_already_selected =
2327 self.selections.disjoint.iter().find(|selection| {
2328 selection.start.to_point(buffer) == start.to_point(buffer)
2329 || selection.end.to_point(buffer) == end.to_point(buffer)
2330 });
2331
2332 clicked_point_already_selected.map(|selection| selection.id)
2333 }
2334 };
2335
2336 let selections_count = self.selections.count();
2337
2338 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2339 if let Some(point_to_delete) = point_to_delete {
2340 s.delete(point_to_delete);
2341
2342 if selections_count == 1 {
2343 s.set_pending_anchor_range(start..end, mode);
2344 }
2345 } else {
2346 if !add {
2347 s.clear_disjoint();
2348 } else if click_count > 1 {
2349 s.delete(newest_selection.id)
2350 }
2351
2352 s.set_pending_anchor_range(start..end, mode);
2353 }
2354 });
2355 }
2356
2357 fn begin_columnar_selection(
2358 &mut self,
2359 position: DisplayPoint,
2360 goal_column: u32,
2361 reset: bool,
2362 window: &mut Window,
2363 cx: &mut Context<Self>,
2364 ) {
2365 if !self.focus_handle.is_focused(window) {
2366 self.last_focused_descendant = None;
2367 window.focus(&self.focus_handle);
2368 }
2369
2370 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2371
2372 if reset {
2373 let pointer_position = display_map
2374 .buffer_snapshot
2375 .anchor_before(position.to_point(&display_map));
2376
2377 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2378 s.clear_disjoint();
2379 s.set_pending_anchor_range(
2380 pointer_position..pointer_position,
2381 SelectMode::Character,
2382 );
2383 });
2384 }
2385
2386 let tail = self.selections.newest::<Point>(cx).tail();
2387 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2388
2389 if !reset {
2390 self.select_columns(
2391 tail.to_display_point(&display_map),
2392 position,
2393 goal_column,
2394 &display_map,
2395 window,
2396 cx,
2397 );
2398 }
2399 }
2400
2401 fn update_selection(
2402 &mut self,
2403 position: DisplayPoint,
2404 goal_column: u32,
2405 scroll_delta: gpui::Point<f32>,
2406 window: &mut Window,
2407 cx: &mut Context<Self>,
2408 ) {
2409 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2410
2411 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2412 let tail = tail.to_display_point(&display_map);
2413 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2414 } else if let Some(mut pending) = self.selections.pending_anchor() {
2415 let buffer = self.buffer.read(cx).snapshot(cx);
2416 let head;
2417 let tail;
2418 let mode = self.selections.pending_mode().unwrap();
2419 match &mode {
2420 SelectMode::Character => {
2421 head = position.to_point(&display_map);
2422 tail = pending.tail().to_point(&buffer);
2423 }
2424 SelectMode::Word(original_range) => {
2425 let original_display_range = original_range.start.to_display_point(&display_map)
2426 ..original_range.end.to_display_point(&display_map);
2427 let original_buffer_range = original_display_range.start.to_point(&display_map)
2428 ..original_display_range.end.to_point(&display_map);
2429 if movement::is_inside_word(&display_map, position)
2430 || original_display_range.contains(&position)
2431 {
2432 let word_range = movement::surrounding_word(&display_map, position);
2433 if word_range.start < original_display_range.start {
2434 head = word_range.start.to_point(&display_map);
2435 } else {
2436 head = word_range.end.to_point(&display_map);
2437 }
2438 } else {
2439 head = position.to_point(&display_map);
2440 }
2441
2442 if head <= original_buffer_range.start {
2443 tail = original_buffer_range.end;
2444 } else {
2445 tail = original_buffer_range.start;
2446 }
2447 }
2448 SelectMode::Line(original_range) => {
2449 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2450
2451 let position = display_map
2452 .clip_point(position, Bias::Left)
2453 .to_point(&display_map);
2454 let line_start = display_map.prev_line_boundary(position).0;
2455 let next_line_start = buffer.clip_point(
2456 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2457 Bias::Left,
2458 );
2459
2460 if line_start < original_range.start {
2461 head = line_start
2462 } else {
2463 head = next_line_start
2464 }
2465
2466 if head <= original_range.start {
2467 tail = original_range.end;
2468 } else {
2469 tail = original_range.start;
2470 }
2471 }
2472 SelectMode::All => {
2473 return;
2474 }
2475 };
2476
2477 if head < tail {
2478 pending.start = buffer.anchor_before(head);
2479 pending.end = buffer.anchor_before(tail);
2480 pending.reversed = true;
2481 } else {
2482 pending.start = buffer.anchor_before(tail);
2483 pending.end = buffer.anchor_before(head);
2484 pending.reversed = false;
2485 }
2486
2487 self.change_selections(None, window, cx, |s| {
2488 s.set_pending(pending, mode);
2489 });
2490 } else {
2491 log::error!("update_selection dispatched with no pending selection");
2492 return;
2493 }
2494
2495 self.apply_scroll_delta(scroll_delta, window, cx);
2496 cx.notify();
2497 }
2498
2499 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2500 self.columnar_selection_tail.take();
2501 if self.selections.pending_anchor().is_some() {
2502 let selections = self.selections.all::<usize>(cx);
2503 self.change_selections(None, window, cx, |s| {
2504 s.select(selections);
2505 s.clear_pending();
2506 });
2507 }
2508 }
2509
2510 fn select_columns(
2511 &mut self,
2512 tail: DisplayPoint,
2513 head: DisplayPoint,
2514 goal_column: u32,
2515 display_map: &DisplaySnapshot,
2516 window: &mut Window,
2517 cx: &mut Context<Self>,
2518 ) {
2519 let start_row = cmp::min(tail.row(), head.row());
2520 let end_row = cmp::max(tail.row(), head.row());
2521 let start_column = cmp::min(tail.column(), goal_column);
2522 let end_column = cmp::max(tail.column(), goal_column);
2523 let reversed = start_column < tail.column();
2524
2525 let selection_ranges = (start_row.0..=end_row.0)
2526 .map(DisplayRow)
2527 .filter_map(|row| {
2528 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2529 let start = display_map
2530 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2531 .to_point(display_map);
2532 let end = display_map
2533 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2534 .to_point(display_map);
2535 if reversed {
2536 Some(end..start)
2537 } else {
2538 Some(start..end)
2539 }
2540 } else {
2541 None
2542 }
2543 })
2544 .collect::<Vec<_>>();
2545
2546 self.change_selections(None, window, cx, |s| {
2547 s.select_ranges(selection_ranges);
2548 });
2549 cx.notify();
2550 }
2551
2552 pub fn has_pending_nonempty_selection(&self) -> bool {
2553 let pending_nonempty_selection = match self.selections.pending_anchor() {
2554 Some(Selection { start, end, .. }) => start != end,
2555 None => false,
2556 };
2557
2558 pending_nonempty_selection
2559 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2560 }
2561
2562 pub fn has_pending_selection(&self) -> bool {
2563 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2564 }
2565
2566 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2567 self.selection_mark_mode = false;
2568
2569 if self.clear_expanded_diff_hunks(cx) {
2570 cx.notify();
2571 return;
2572 }
2573 if self.dismiss_menus_and_popups(true, window, cx) {
2574 return;
2575 }
2576
2577 if self.mode == EditorMode::Full
2578 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2579 {
2580 return;
2581 }
2582
2583 cx.propagate();
2584 }
2585
2586 pub fn dismiss_menus_and_popups(
2587 &mut self,
2588 should_report_inline_completion_event: bool,
2589 window: &mut Window,
2590 cx: &mut Context<Self>,
2591 ) -> bool {
2592 if self.take_rename(false, window, cx).is_some() {
2593 return true;
2594 }
2595
2596 if hide_hover(self, cx) {
2597 return true;
2598 }
2599
2600 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2601 return true;
2602 }
2603
2604 if self.hide_context_menu(window, cx).is_some() {
2605 return true;
2606 }
2607
2608 if self.mouse_context_menu.take().is_some() {
2609 return true;
2610 }
2611
2612 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2613 return true;
2614 }
2615
2616 if self.snippet_stack.pop().is_some() {
2617 return true;
2618 }
2619
2620 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2621 self.dismiss_diagnostics(cx);
2622 return true;
2623 }
2624
2625 false
2626 }
2627
2628 fn linked_editing_ranges_for(
2629 &self,
2630 selection: Range<text::Anchor>,
2631 cx: &App,
2632 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2633 if self.linked_edit_ranges.is_empty() {
2634 return None;
2635 }
2636 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2637 selection.end.buffer_id.and_then(|end_buffer_id| {
2638 if selection.start.buffer_id != Some(end_buffer_id) {
2639 return None;
2640 }
2641 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2642 let snapshot = buffer.read(cx).snapshot();
2643 self.linked_edit_ranges
2644 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2645 .map(|ranges| (ranges, snapshot, buffer))
2646 })?;
2647 use text::ToOffset as TO;
2648 // find offset from the start of current range to current cursor position
2649 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2650
2651 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2652 let start_difference = start_offset - start_byte_offset;
2653 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2654 let end_difference = end_offset - start_byte_offset;
2655 // Current range has associated linked ranges.
2656 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2657 for range in linked_ranges.iter() {
2658 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2659 let end_offset = start_offset + end_difference;
2660 let start_offset = start_offset + start_difference;
2661 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2662 continue;
2663 }
2664 if self.selections.disjoint_anchor_ranges().any(|s| {
2665 if s.start.buffer_id != selection.start.buffer_id
2666 || s.end.buffer_id != selection.end.buffer_id
2667 {
2668 return false;
2669 }
2670 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2671 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2672 }) {
2673 continue;
2674 }
2675 let start = buffer_snapshot.anchor_after(start_offset);
2676 let end = buffer_snapshot.anchor_after(end_offset);
2677 linked_edits
2678 .entry(buffer.clone())
2679 .or_default()
2680 .push(start..end);
2681 }
2682 Some(linked_edits)
2683 }
2684
2685 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2686 let text: Arc<str> = text.into();
2687
2688 if self.read_only(cx) {
2689 return;
2690 }
2691
2692 let selections = self.selections.all_adjusted(cx);
2693 let mut bracket_inserted = false;
2694 let mut edits = Vec::new();
2695 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2696 let mut new_selections = Vec::with_capacity(selections.len());
2697 let mut new_autoclose_regions = Vec::new();
2698 let snapshot = self.buffer.read(cx).read(cx);
2699
2700 for (selection, autoclose_region) in
2701 self.selections_with_autoclose_regions(selections, &snapshot)
2702 {
2703 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2704 // Determine if the inserted text matches the opening or closing
2705 // bracket of any of this language's bracket pairs.
2706 let mut bracket_pair = None;
2707 let mut is_bracket_pair_start = false;
2708 let mut is_bracket_pair_end = false;
2709 if !text.is_empty() {
2710 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2711 // and they are removing the character that triggered IME popup.
2712 for (pair, enabled) in scope.brackets() {
2713 if !pair.close && !pair.surround {
2714 continue;
2715 }
2716
2717 if enabled && pair.start.ends_with(text.as_ref()) {
2718 let prefix_len = pair.start.len() - text.len();
2719 let preceding_text_matches_prefix = prefix_len == 0
2720 || (selection.start.column >= (prefix_len as u32)
2721 && snapshot.contains_str_at(
2722 Point::new(
2723 selection.start.row,
2724 selection.start.column - (prefix_len as u32),
2725 ),
2726 &pair.start[..prefix_len],
2727 ));
2728 if preceding_text_matches_prefix {
2729 bracket_pair = Some(pair.clone());
2730 is_bracket_pair_start = true;
2731 break;
2732 }
2733 }
2734 if pair.end.as_str() == text.as_ref() {
2735 bracket_pair = Some(pair.clone());
2736 is_bracket_pair_end = true;
2737 break;
2738 }
2739 }
2740 }
2741
2742 if let Some(bracket_pair) = bracket_pair {
2743 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2744 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2745 let auto_surround =
2746 self.use_auto_surround && snapshot_settings.use_auto_surround;
2747 if selection.is_empty() {
2748 if is_bracket_pair_start {
2749 // If the inserted text is a suffix of an opening bracket and the
2750 // selection is preceded by the rest of the opening bracket, then
2751 // insert the closing bracket.
2752 let following_text_allows_autoclose = snapshot
2753 .chars_at(selection.start)
2754 .next()
2755 .map_or(true, |c| scope.should_autoclose_before(c));
2756
2757 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2758 && bracket_pair.start.len() == 1
2759 {
2760 let target = bracket_pair.start.chars().next().unwrap();
2761 let current_line_count = snapshot
2762 .reversed_chars_at(selection.start)
2763 .take_while(|&c| c != '\n')
2764 .filter(|&c| c == target)
2765 .count();
2766 current_line_count % 2 == 1
2767 } else {
2768 false
2769 };
2770
2771 if autoclose
2772 && bracket_pair.close
2773 && following_text_allows_autoclose
2774 && !is_closing_quote
2775 {
2776 let anchor = snapshot.anchor_before(selection.end);
2777 new_selections.push((selection.map(|_| anchor), text.len()));
2778 new_autoclose_regions.push((
2779 anchor,
2780 text.len(),
2781 selection.id,
2782 bracket_pair.clone(),
2783 ));
2784 edits.push((
2785 selection.range(),
2786 format!("{}{}", text, bracket_pair.end).into(),
2787 ));
2788 bracket_inserted = true;
2789 continue;
2790 }
2791 }
2792
2793 if let Some(region) = autoclose_region {
2794 // If the selection is followed by an auto-inserted closing bracket,
2795 // then don't insert that closing bracket again; just move the selection
2796 // past the closing bracket.
2797 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2798 && text.as_ref() == region.pair.end.as_str();
2799 if should_skip {
2800 let anchor = snapshot.anchor_after(selection.end);
2801 new_selections
2802 .push((selection.map(|_| anchor), region.pair.end.len()));
2803 continue;
2804 }
2805 }
2806
2807 let always_treat_brackets_as_autoclosed = snapshot
2808 .settings_at(selection.start, cx)
2809 .always_treat_brackets_as_autoclosed;
2810 if always_treat_brackets_as_autoclosed
2811 && is_bracket_pair_end
2812 && snapshot.contains_str_at(selection.end, text.as_ref())
2813 {
2814 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2815 // and the inserted text is a closing bracket and the selection is followed
2816 // by the closing bracket then move the selection past the closing bracket.
2817 let anchor = snapshot.anchor_after(selection.end);
2818 new_selections.push((selection.map(|_| anchor), text.len()));
2819 continue;
2820 }
2821 }
2822 // If an opening bracket is 1 character long and is typed while
2823 // text is selected, then surround that text with the bracket pair.
2824 else if auto_surround
2825 && bracket_pair.surround
2826 && is_bracket_pair_start
2827 && bracket_pair.start.chars().count() == 1
2828 {
2829 edits.push((selection.start..selection.start, text.clone()));
2830 edits.push((
2831 selection.end..selection.end,
2832 bracket_pair.end.as_str().into(),
2833 ));
2834 bracket_inserted = true;
2835 new_selections.push((
2836 Selection {
2837 id: selection.id,
2838 start: snapshot.anchor_after(selection.start),
2839 end: snapshot.anchor_before(selection.end),
2840 reversed: selection.reversed,
2841 goal: selection.goal,
2842 },
2843 0,
2844 ));
2845 continue;
2846 }
2847 }
2848 }
2849
2850 if self.auto_replace_emoji_shortcode
2851 && selection.is_empty()
2852 && text.as_ref().ends_with(':')
2853 {
2854 if let Some(possible_emoji_short_code) =
2855 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2856 {
2857 if !possible_emoji_short_code.is_empty() {
2858 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2859 let emoji_shortcode_start = Point::new(
2860 selection.start.row,
2861 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2862 );
2863
2864 // Remove shortcode from buffer
2865 edits.push((
2866 emoji_shortcode_start..selection.start,
2867 "".to_string().into(),
2868 ));
2869 new_selections.push((
2870 Selection {
2871 id: selection.id,
2872 start: snapshot.anchor_after(emoji_shortcode_start),
2873 end: snapshot.anchor_before(selection.start),
2874 reversed: selection.reversed,
2875 goal: selection.goal,
2876 },
2877 0,
2878 ));
2879
2880 // Insert emoji
2881 let selection_start_anchor = snapshot.anchor_after(selection.start);
2882 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2883 edits.push((selection.start..selection.end, emoji.to_string().into()));
2884
2885 continue;
2886 }
2887 }
2888 }
2889 }
2890
2891 // If not handling any auto-close operation, then just replace the selected
2892 // text with the given input and move the selection to the end of the
2893 // newly inserted text.
2894 let anchor = snapshot.anchor_after(selection.end);
2895 if !self.linked_edit_ranges.is_empty() {
2896 let start_anchor = snapshot.anchor_before(selection.start);
2897
2898 let is_word_char = text.chars().next().map_or(true, |char| {
2899 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2900 classifier.is_word(char)
2901 });
2902
2903 if is_word_char {
2904 if let Some(ranges) = self
2905 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2906 {
2907 for (buffer, edits) in ranges {
2908 linked_edits
2909 .entry(buffer.clone())
2910 .or_default()
2911 .extend(edits.into_iter().map(|range| (range, text.clone())));
2912 }
2913 }
2914 }
2915 }
2916
2917 new_selections.push((selection.map(|_| anchor), 0));
2918 edits.push((selection.start..selection.end, text.clone()));
2919 }
2920
2921 drop(snapshot);
2922
2923 self.transact(window, cx, |this, window, cx| {
2924 this.buffer.update(cx, |buffer, cx| {
2925 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2926 });
2927 for (buffer, edits) in linked_edits {
2928 buffer.update(cx, |buffer, cx| {
2929 let snapshot = buffer.snapshot();
2930 let edits = edits
2931 .into_iter()
2932 .map(|(range, text)| {
2933 use text::ToPoint as TP;
2934 let end_point = TP::to_point(&range.end, &snapshot);
2935 let start_point = TP::to_point(&range.start, &snapshot);
2936 (start_point..end_point, text)
2937 })
2938 .sorted_by_key(|(range, _)| range.start)
2939 .collect::<Vec<_>>();
2940 buffer.edit(edits, None, cx);
2941 })
2942 }
2943 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2944 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2945 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2946 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2947 .zip(new_selection_deltas)
2948 .map(|(selection, delta)| Selection {
2949 id: selection.id,
2950 start: selection.start + delta,
2951 end: selection.end + delta,
2952 reversed: selection.reversed,
2953 goal: SelectionGoal::None,
2954 })
2955 .collect::<Vec<_>>();
2956
2957 let mut i = 0;
2958 for (position, delta, selection_id, pair) in new_autoclose_regions {
2959 let position = position.to_offset(&map.buffer_snapshot) + delta;
2960 let start = map.buffer_snapshot.anchor_before(position);
2961 let end = map.buffer_snapshot.anchor_after(position);
2962 while let Some(existing_state) = this.autoclose_regions.get(i) {
2963 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
2964 Ordering::Less => i += 1,
2965 Ordering::Greater => break,
2966 Ordering::Equal => {
2967 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
2968 Ordering::Less => i += 1,
2969 Ordering::Equal => break,
2970 Ordering::Greater => break,
2971 }
2972 }
2973 }
2974 }
2975 this.autoclose_regions.insert(
2976 i,
2977 AutocloseRegion {
2978 selection_id,
2979 range: start..end,
2980 pair,
2981 },
2982 );
2983 }
2984
2985 let had_active_inline_completion = this.has_active_inline_completion();
2986 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
2987 s.select(new_selections)
2988 });
2989
2990 if !bracket_inserted {
2991 if let Some(on_type_format_task) =
2992 this.trigger_on_type_formatting(text.to_string(), window, cx)
2993 {
2994 on_type_format_task.detach_and_log_err(cx);
2995 }
2996 }
2997
2998 let editor_settings = EditorSettings::get_global(cx);
2999 if bracket_inserted
3000 && (editor_settings.auto_signature_help
3001 || editor_settings.show_signature_help_after_edits)
3002 {
3003 this.show_signature_help(&ShowSignatureHelp, window, cx);
3004 }
3005
3006 let trigger_in_words =
3007 this.show_inline_completions_in_menu(cx) || !had_active_inline_completion;
3008 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3009 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3010 this.refresh_inline_completion(true, false, window, cx);
3011 });
3012 }
3013
3014 fn find_possible_emoji_shortcode_at_position(
3015 snapshot: &MultiBufferSnapshot,
3016 position: Point,
3017 ) -> Option<String> {
3018 let mut chars = Vec::new();
3019 let mut found_colon = false;
3020 for char in snapshot.reversed_chars_at(position).take(100) {
3021 // Found a possible emoji shortcode in the middle of the buffer
3022 if found_colon {
3023 if char.is_whitespace() {
3024 chars.reverse();
3025 return Some(chars.iter().collect());
3026 }
3027 // If the previous character is not a whitespace, we are in the middle of a word
3028 // and we only want to complete the shortcode if the word is made up of other emojis
3029 let mut containing_word = String::new();
3030 for ch in snapshot
3031 .reversed_chars_at(position)
3032 .skip(chars.len() + 1)
3033 .take(100)
3034 {
3035 if ch.is_whitespace() {
3036 break;
3037 }
3038 containing_word.push(ch);
3039 }
3040 let containing_word = containing_word.chars().rev().collect::<String>();
3041 if util::word_consists_of_emojis(containing_word.as_str()) {
3042 chars.reverse();
3043 return Some(chars.iter().collect());
3044 }
3045 }
3046
3047 if char.is_whitespace() || !char.is_ascii() {
3048 return None;
3049 }
3050 if char == ':' {
3051 found_colon = true;
3052 } else {
3053 chars.push(char);
3054 }
3055 }
3056 // Found a possible emoji shortcode at the beginning of the buffer
3057 chars.reverse();
3058 Some(chars.iter().collect())
3059 }
3060
3061 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3062 self.transact(window, cx, |this, window, cx| {
3063 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3064 let selections = this.selections.all::<usize>(cx);
3065 let multi_buffer = this.buffer.read(cx);
3066 let buffer = multi_buffer.snapshot(cx);
3067 selections
3068 .iter()
3069 .map(|selection| {
3070 let start_point = selection.start.to_point(&buffer);
3071 let mut indent =
3072 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3073 indent.len = cmp::min(indent.len, start_point.column);
3074 let start = selection.start;
3075 let end = selection.end;
3076 let selection_is_empty = start == end;
3077 let language_scope = buffer.language_scope_at(start);
3078 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3079 &language_scope
3080 {
3081 let leading_whitespace_len = buffer
3082 .reversed_chars_at(start)
3083 .take_while(|c| c.is_whitespace() && *c != '\n')
3084 .map(|c| c.len_utf8())
3085 .sum::<usize>();
3086
3087 let trailing_whitespace_len = buffer
3088 .chars_at(end)
3089 .take_while(|c| c.is_whitespace() && *c != '\n')
3090 .map(|c| c.len_utf8())
3091 .sum::<usize>();
3092
3093 let insert_extra_newline =
3094 language.brackets().any(|(pair, enabled)| {
3095 let pair_start = pair.start.trim_end();
3096 let pair_end = pair.end.trim_start();
3097
3098 enabled
3099 && pair.newline
3100 && buffer.contains_str_at(
3101 end + trailing_whitespace_len,
3102 pair_end,
3103 )
3104 && buffer.contains_str_at(
3105 (start - leading_whitespace_len)
3106 .saturating_sub(pair_start.len()),
3107 pair_start,
3108 )
3109 });
3110
3111 // Comment extension on newline is allowed only for cursor selections
3112 let comment_delimiter = maybe!({
3113 if !selection_is_empty {
3114 return None;
3115 }
3116
3117 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3118 return None;
3119 }
3120
3121 let delimiters = language.line_comment_prefixes();
3122 let max_len_of_delimiter =
3123 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3124 let (snapshot, range) =
3125 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3126
3127 let mut index_of_first_non_whitespace = 0;
3128 let comment_candidate = snapshot
3129 .chars_for_range(range)
3130 .skip_while(|c| {
3131 let should_skip = c.is_whitespace();
3132 if should_skip {
3133 index_of_first_non_whitespace += 1;
3134 }
3135 should_skip
3136 })
3137 .take(max_len_of_delimiter)
3138 .collect::<String>();
3139 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3140 comment_candidate.starts_with(comment_prefix.as_ref())
3141 })?;
3142 let cursor_is_placed_after_comment_marker =
3143 index_of_first_non_whitespace + comment_prefix.len()
3144 <= start_point.column as usize;
3145 if cursor_is_placed_after_comment_marker {
3146 Some(comment_prefix.clone())
3147 } else {
3148 None
3149 }
3150 });
3151 (comment_delimiter, insert_extra_newline)
3152 } else {
3153 (None, false)
3154 };
3155
3156 let capacity_for_delimiter = comment_delimiter
3157 .as_deref()
3158 .map(str::len)
3159 .unwrap_or_default();
3160 let mut new_text =
3161 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3162 new_text.push('\n');
3163 new_text.extend(indent.chars());
3164 if let Some(delimiter) = &comment_delimiter {
3165 new_text.push_str(delimiter);
3166 }
3167 if insert_extra_newline {
3168 new_text = new_text.repeat(2);
3169 }
3170
3171 let anchor = buffer.anchor_after(end);
3172 let new_selection = selection.map(|_| anchor);
3173 (
3174 (start..end, new_text),
3175 (insert_extra_newline, new_selection),
3176 )
3177 })
3178 .unzip()
3179 };
3180
3181 this.edit_with_autoindent(edits, cx);
3182 let buffer = this.buffer.read(cx).snapshot(cx);
3183 let new_selections = selection_fixup_info
3184 .into_iter()
3185 .map(|(extra_newline_inserted, new_selection)| {
3186 let mut cursor = new_selection.end.to_point(&buffer);
3187 if extra_newline_inserted {
3188 cursor.row -= 1;
3189 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3190 }
3191 new_selection.map(|_| cursor)
3192 })
3193 .collect();
3194
3195 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3196 s.select(new_selections)
3197 });
3198 this.refresh_inline_completion(true, false, window, cx);
3199 });
3200 }
3201
3202 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3203 let buffer = self.buffer.read(cx);
3204 let snapshot = buffer.snapshot(cx);
3205
3206 let mut edits = Vec::new();
3207 let mut rows = Vec::new();
3208
3209 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3210 let cursor = selection.head();
3211 let row = cursor.row;
3212
3213 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3214
3215 let newline = "\n".to_string();
3216 edits.push((start_of_line..start_of_line, newline));
3217
3218 rows.push(row + rows_inserted as u32);
3219 }
3220
3221 self.transact(window, cx, |editor, window, cx| {
3222 editor.edit(edits, cx);
3223
3224 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3225 let mut index = 0;
3226 s.move_cursors_with(|map, _, _| {
3227 let row = rows[index];
3228 index += 1;
3229
3230 let point = Point::new(row, 0);
3231 let boundary = map.next_line_boundary(point).1;
3232 let clipped = map.clip_point(boundary, Bias::Left);
3233
3234 (clipped, SelectionGoal::None)
3235 });
3236 });
3237
3238 let mut indent_edits = Vec::new();
3239 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3240 for row in rows {
3241 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3242 for (row, indent) in indents {
3243 if indent.len == 0 {
3244 continue;
3245 }
3246
3247 let text = match indent.kind {
3248 IndentKind::Space => " ".repeat(indent.len as usize),
3249 IndentKind::Tab => "\t".repeat(indent.len as usize),
3250 };
3251 let point = Point::new(row.0, 0);
3252 indent_edits.push((point..point, text));
3253 }
3254 }
3255 editor.edit(indent_edits, cx);
3256 });
3257 }
3258
3259 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3260 let buffer = self.buffer.read(cx);
3261 let snapshot = buffer.snapshot(cx);
3262
3263 let mut edits = Vec::new();
3264 let mut rows = Vec::new();
3265 let mut rows_inserted = 0;
3266
3267 for selection in self.selections.all_adjusted(cx) {
3268 let cursor = selection.head();
3269 let row = cursor.row;
3270
3271 let point = Point::new(row + 1, 0);
3272 let start_of_line = snapshot.clip_point(point, Bias::Left);
3273
3274 let newline = "\n".to_string();
3275 edits.push((start_of_line..start_of_line, newline));
3276
3277 rows_inserted += 1;
3278 rows.push(row + rows_inserted);
3279 }
3280
3281 self.transact(window, cx, |editor, window, cx| {
3282 editor.edit(edits, cx);
3283
3284 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3285 let mut index = 0;
3286 s.move_cursors_with(|map, _, _| {
3287 let row = rows[index];
3288 index += 1;
3289
3290 let point = Point::new(row, 0);
3291 let boundary = map.next_line_boundary(point).1;
3292 let clipped = map.clip_point(boundary, Bias::Left);
3293
3294 (clipped, SelectionGoal::None)
3295 });
3296 });
3297
3298 let mut indent_edits = Vec::new();
3299 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3300 for row in rows {
3301 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3302 for (row, indent) in indents {
3303 if indent.len == 0 {
3304 continue;
3305 }
3306
3307 let text = match indent.kind {
3308 IndentKind::Space => " ".repeat(indent.len as usize),
3309 IndentKind::Tab => "\t".repeat(indent.len as usize),
3310 };
3311 let point = Point::new(row.0, 0);
3312 indent_edits.push((point..point, text));
3313 }
3314 }
3315 editor.edit(indent_edits, cx);
3316 });
3317 }
3318
3319 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3320 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3321 original_indent_columns: Vec::new(),
3322 });
3323 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3324 }
3325
3326 fn insert_with_autoindent_mode(
3327 &mut self,
3328 text: &str,
3329 autoindent_mode: Option<AutoindentMode>,
3330 window: &mut Window,
3331 cx: &mut Context<Self>,
3332 ) {
3333 if self.read_only(cx) {
3334 return;
3335 }
3336
3337 let text: Arc<str> = text.into();
3338 self.transact(window, cx, |this, window, cx| {
3339 let old_selections = this.selections.all_adjusted(cx);
3340 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3341 let anchors = {
3342 let snapshot = buffer.read(cx);
3343 old_selections
3344 .iter()
3345 .map(|s| {
3346 let anchor = snapshot.anchor_after(s.head());
3347 s.map(|_| anchor)
3348 })
3349 .collect::<Vec<_>>()
3350 };
3351 buffer.edit(
3352 old_selections
3353 .iter()
3354 .map(|s| (s.start..s.end, text.clone())),
3355 autoindent_mode,
3356 cx,
3357 );
3358 anchors
3359 });
3360
3361 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3362 s.select_anchors(selection_anchors);
3363 });
3364
3365 cx.notify();
3366 });
3367 }
3368
3369 fn trigger_completion_on_input(
3370 &mut self,
3371 text: &str,
3372 trigger_in_words: bool,
3373 window: &mut Window,
3374 cx: &mut Context<Self>,
3375 ) {
3376 if self.is_completion_trigger(text, trigger_in_words, cx) {
3377 self.show_completions(
3378 &ShowCompletions {
3379 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3380 },
3381 window,
3382 cx,
3383 );
3384 } else {
3385 self.hide_context_menu(window, cx);
3386 }
3387 }
3388
3389 fn is_completion_trigger(
3390 &self,
3391 text: &str,
3392 trigger_in_words: bool,
3393 cx: &mut Context<Self>,
3394 ) -> bool {
3395 let position = self.selections.newest_anchor().head();
3396 let multibuffer = self.buffer.read(cx);
3397 let Some(buffer) = position
3398 .buffer_id
3399 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3400 else {
3401 return false;
3402 };
3403
3404 if let Some(completion_provider) = &self.completion_provider {
3405 completion_provider.is_completion_trigger(
3406 &buffer,
3407 position.text_anchor,
3408 text,
3409 trigger_in_words,
3410 cx,
3411 )
3412 } else {
3413 false
3414 }
3415 }
3416
3417 /// If any empty selections is touching the start of its innermost containing autoclose
3418 /// region, expand it to select the brackets.
3419 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3420 let selections = self.selections.all::<usize>(cx);
3421 let buffer = self.buffer.read(cx).read(cx);
3422 let new_selections = self
3423 .selections_with_autoclose_regions(selections, &buffer)
3424 .map(|(mut selection, region)| {
3425 if !selection.is_empty() {
3426 return selection;
3427 }
3428
3429 if let Some(region) = region {
3430 let mut range = region.range.to_offset(&buffer);
3431 if selection.start == range.start && range.start >= region.pair.start.len() {
3432 range.start -= region.pair.start.len();
3433 if buffer.contains_str_at(range.start, ®ion.pair.start)
3434 && buffer.contains_str_at(range.end, ®ion.pair.end)
3435 {
3436 range.end += region.pair.end.len();
3437 selection.start = range.start;
3438 selection.end = range.end;
3439
3440 return selection;
3441 }
3442 }
3443 }
3444
3445 let always_treat_brackets_as_autoclosed = buffer
3446 .settings_at(selection.start, cx)
3447 .always_treat_brackets_as_autoclosed;
3448
3449 if !always_treat_brackets_as_autoclosed {
3450 return selection;
3451 }
3452
3453 if let Some(scope) = buffer.language_scope_at(selection.start) {
3454 for (pair, enabled) in scope.brackets() {
3455 if !enabled || !pair.close {
3456 continue;
3457 }
3458
3459 if buffer.contains_str_at(selection.start, &pair.end) {
3460 let pair_start_len = pair.start.len();
3461 if buffer.contains_str_at(
3462 selection.start.saturating_sub(pair_start_len),
3463 &pair.start,
3464 ) {
3465 selection.start -= pair_start_len;
3466 selection.end += pair.end.len();
3467
3468 return selection;
3469 }
3470 }
3471 }
3472 }
3473
3474 selection
3475 })
3476 .collect();
3477
3478 drop(buffer);
3479 self.change_selections(None, window, cx, |selections| {
3480 selections.select(new_selections)
3481 });
3482 }
3483
3484 /// Iterate the given selections, and for each one, find the smallest surrounding
3485 /// autoclose region. This uses the ordering of the selections and the autoclose
3486 /// regions to avoid repeated comparisons.
3487 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3488 &'a self,
3489 selections: impl IntoIterator<Item = Selection<D>>,
3490 buffer: &'a MultiBufferSnapshot,
3491 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3492 let mut i = 0;
3493 let mut regions = self.autoclose_regions.as_slice();
3494 selections.into_iter().map(move |selection| {
3495 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3496
3497 let mut enclosing = None;
3498 while let Some(pair_state) = regions.get(i) {
3499 if pair_state.range.end.to_offset(buffer) < range.start {
3500 regions = ®ions[i + 1..];
3501 i = 0;
3502 } else if pair_state.range.start.to_offset(buffer) > range.end {
3503 break;
3504 } else {
3505 if pair_state.selection_id == selection.id {
3506 enclosing = Some(pair_state);
3507 }
3508 i += 1;
3509 }
3510 }
3511
3512 (selection, enclosing)
3513 })
3514 }
3515
3516 /// Remove any autoclose regions that no longer contain their selection.
3517 fn invalidate_autoclose_regions(
3518 &mut self,
3519 mut selections: &[Selection<Anchor>],
3520 buffer: &MultiBufferSnapshot,
3521 ) {
3522 self.autoclose_regions.retain(|state| {
3523 let mut i = 0;
3524 while let Some(selection) = selections.get(i) {
3525 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3526 selections = &selections[1..];
3527 continue;
3528 }
3529 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3530 break;
3531 }
3532 if selection.id == state.selection_id {
3533 return true;
3534 } else {
3535 i += 1;
3536 }
3537 }
3538 false
3539 });
3540 }
3541
3542 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3543 let offset = position.to_offset(buffer);
3544 let (word_range, kind) = buffer.surrounding_word(offset, true);
3545 if offset > word_range.start && kind == Some(CharKind::Word) {
3546 Some(
3547 buffer
3548 .text_for_range(word_range.start..offset)
3549 .collect::<String>(),
3550 )
3551 } else {
3552 None
3553 }
3554 }
3555
3556 pub fn toggle_inlay_hints(
3557 &mut self,
3558 _: &ToggleInlayHints,
3559 _: &mut Window,
3560 cx: &mut Context<Self>,
3561 ) {
3562 self.refresh_inlay_hints(
3563 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3564 cx,
3565 );
3566 }
3567
3568 pub fn inlay_hints_enabled(&self) -> bool {
3569 self.inlay_hint_cache.enabled
3570 }
3571
3572 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3573 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3574 return;
3575 }
3576
3577 let reason_description = reason.description();
3578 let ignore_debounce = matches!(
3579 reason,
3580 InlayHintRefreshReason::SettingsChange(_)
3581 | InlayHintRefreshReason::Toggle(_)
3582 | InlayHintRefreshReason::ExcerptsRemoved(_)
3583 );
3584 let (invalidate_cache, required_languages) = match reason {
3585 InlayHintRefreshReason::Toggle(enabled) => {
3586 self.inlay_hint_cache.enabled = enabled;
3587 if enabled {
3588 (InvalidationStrategy::RefreshRequested, None)
3589 } else {
3590 self.inlay_hint_cache.clear();
3591 self.splice_inlays(
3592 &self
3593 .visible_inlay_hints(cx)
3594 .iter()
3595 .map(|inlay| inlay.id)
3596 .collect::<Vec<InlayId>>(),
3597 Vec::new(),
3598 cx,
3599 );
3600 return;
3601 }
3602 }
3603 InlayHintRefreshReason::SettingsChange(new_settings) => {
3604 match self.inlay_hint_cache.update_settings(
3605 &self.buffer,
3606 new_settings,
3607 self.visible_inlay_hints(cx),
3608 cx,
3609 ) {
3610 ControlFlow::Break(Some(InlaySplice {
3611 to_remove,
3612 to_insert,
3613 })) => {
3614 self.splice_inlays(&to_remove, to_insert, cx);
3615 return;
3616 }
3617 ControlFlow::Break(None) => return,
3618 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3619 }
3620 }
3621 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3622 if let Some(InlaySplice {
3623 to_remove,
3624 to_insert,
3625 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3626 {
3627 self.splice_inlays(&to_remove, to_insert, cx);
3628 }
3629 return;
3630 }
3631 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3632 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3633 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3634 }
3635 InlayHintRefreshReason::RefreshRequested => {
3636 (InvalidationStrategy::RefreshRequested, None)
3637 }
3638 };
3639
3640 if let Some(InlaySplice {
3641 to_remove,
3642 to_insert,
3643 }) = self.inlay_hint_cache.spawn_hint_refresh(
3644 reason_description,
3645 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3646 invalidate_cache,
3647 ignore_debounce,
3648 cx,
3649 ) {
3650 self.splice_inlays(&to_remove, to_insert, cx);
3651 }
3652 }
3653
3654 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3655 self.display_map
3656 .read(cx)
3657 .current_inlays()
3658 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3659 .cloned()
3660 .collect()
3661 }
3662
3663 pub fn excerpts_for_inlay_hints_query(
3664 &self,
3665 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3666 cx: &mut Context<Editor>,
3667 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3668 let Some(project) = self.project.as_ref() else {
3669 return HashMap::default();
3670 };
3671 let project = project.read(cx);
3672 let multi_buffer = self.buffer().read(cx);
3673 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3674 let multi_buffer_visible_start = self
3675 .scroll_manager
3676 .anchor()
3677 .anchor
3678 .to_point(&multi_buffer_snapshot);
3679 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3680 multi_buffer_visible_start
3681 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3682 Bias::Left,
3683 );
3684 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3685 multi_buffer_snapshot
3686 .range_to_buffer_ranges(multi_buffer_visible_range)
3687 .into_iter()
3688 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3689 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3690 let buffer_file = project::File::from_dyn(buffer.file())?;
3691 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3692 let worktree_entry = buffer_worktree
3693 .read(cx)
3694 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3695 if worktree_entry.is_ignored {
3696 return None;
3697 }
3698
3699 let language = buffer.language()?;
3700 if let Some(restrict_to_languages) = restrict_to_languages {
3701 if !restrict_to_languages.contains(language) {
3702 return None;
3703 }
3704 }
3705 Some((
3706 excerpt_id,
3707 (
3708 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3709 buffer.version().clone(),
3710 excerpt_visible_range,
3711 ),
3712 ))
3713 })
3714 .collect()
3715 }
3716
3717 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3718 TextLayoutDetails {
3719 text_system: window.text_system().clone(),
3720 editor_style: self.style.clone().unwrap(),
3721 rem_size: window.rem_size(),
3722 scroll_anchor: self.scroll_manager.anchor(),
3723 visible_rows: self.visible_line_count(),
3724 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3725 }
3726 }
3727
3728 pub fn splice_inlays(
3729 &self,
3730 to_remove: &[InlayId],
3731 to_insert: Vec<Inlay>,
3732 cx: &mut Context<Self>,
3733 ) {
3734 self.display_map.update(cx, |display_map, cx| {
3735 display_map.splice_inlays(to_remove, to_insert, cx)
3736 });
3737 cx.notify();
3738 }
3739
3740 fn trigger_on_type_formatting(
3741 &self,
3742 input: String,
3743 window: &mut Window,
3744 cx: &mut Context<Self>,
3745 ) -> Option<Task<Result<()>>> {
3746 if input.len() != 1 {
3747 return None;
3748 }
3749
3750 let project = self.project.as_ref()?;
3751 let position = self.selections.newest_anchor().head();
3752 let (buffer, buffer_position) = self
3753 .buffer
3754 .read(cx)
3755 .text_anchor_for_position(position, cx)?;
3756
3757 let settings = language_settings::language_settings(
3758 buffer
3759 .read(cx)
3760 .language_at(buffer_position)
3761 .map(|l| l.name()),
3762 buffer.read(cx).file(),
3763 cx,
3764 );
3765 if !settings.use_on_type_format {
3766 return None;
3767 }
3768
3769 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3770 // hence we do LSP request & edit on host side only — add formats to host's history.
3771 let push_to_lsp_host_history = true;
3772 // If this is not the host, append its history with new edits.
3773 let push_to_client_history = project.read(cx).is_via_collab();
3774
3775 let on_type_formatting = project.update(cx, |project, cx| {
3776 project.on_type_format(
3777 buffer.clone(),
3778 buffer_position,
3779 input,
3780 push_to_lsp_host_history,
3781 cx,
3782 )
3783 });
3784 Some(cx.spawn_in(window, |editor, mut cx| async move {
3785 if let Some(transaction) = on_type_formatting.await? {
3786 if push_to_client_history {
3787 buffer
3788 .update(&mut cx, |buffer, _| {
3789 buffer.push_transaction(transaction, Instant::now());
3790 })
3791 .ok();
3792 }
3793 editor.update(&mut cx, |editor, cx| {
3794 editor.refresh_document_highlights(cx);
3795 })?;
3796 }
3797 Ok(())
3798 }))
3799 }
3800
3801 pub fn show_completions(
3802 &mut self,
3803 options: &ShowCompletions,
3804 window: &mut Window,
3805 cx: &mut Context<Self>,
3806 ) {
3807 if self.pending_rename.is_some() {
3808 return;
3809 }
3810
3811 let Some(provider) = self.completion_provider.as_ref() else {
3812 return;
3813 };
3814
3815 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3816 return;
3817 }
3818
3819 let position = self.selections.newest_anchor().head();
3820 if position.diff_base_anchor.is_some() {
3821 return;
3822 }
3823 let (buffer, buffer_position) =
3824 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3825 output
3826 } else {
3827 return;
3828 };
3829 let show_completion_documentation = buffer
3830 .read(cx)
3831 .snapshot()
3832 .settings_at(buffer_position, cx)
3833 .show_completion_documentation;
3834
3835 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3836
3837 let trigger_kind = match &options.trigger {
3838 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3839 CompletionTriggerKind::TRIGGER_CHARACTER
3840 }
3841 _ => CompletionTriggerKind::INVOKED,
3842 };
3843 let completion_context = CompletionContext {
3844 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3845 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3846 Some(String::from(trigger))
3847 } else {
3848 None
3849 }
3850 }),
3851 trigger_kind,
3852 };
3853 let completions =
3854 provider.completions(&buffer, buffer_position, completion_context, window, cx);
3855 let sort_completions = provider.sort_completions();
3856
3857 let id = post_inc(&mut self.next_completion_id);
3858 let task = cx.spawn_in(window, |editor, mut cx| {
3859 async move {
3860 editor.update(&mut cx, |this, _| {
3861 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3862 })?;
3863 let completions = completions.await.log_err();
3864 let menu = if let Some(completions) = completions {
3865 let mut menu = CompletionsMenu::new(
3866 id,
3867 sort_completions,
3868 show_completion_documentation,
3869 position,
3870 buffer.clone(),
3871 completions.into(),
3872 );
3873
3874 menu.filter(query.as_deref(), cx.background_executor().clone())
3875 .await;
3876
3877 menu.visible().then_some(menu)
3878 } else {
3879 None
3880 };
3881
3882 editor.update_in(&mut cx, |editor, window, cx| {
3883 match editor.context_menu.borrow().as_ref() {
3884 None => {}
3885 Some(CodeContextMenu::Completions(prev_menu)) => {
3886 if prev_menu.id > id {
3887 return;
3888 }
3889 }
3890 _ => return,
3891 }
3892
3893 if editor.focus_handle.is_focused(window) && menu.is_some() {
3894 let mut menu = menu.unwrap();
3895 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3896
3897 *editor.context_menu.borrow_mut() =
3898 Some(CodeContextMenu::Completions(menu));
3899
3900 if editor.show_inline_completions_in_menu(cx) {
3901 editor.update_visible_inline_completion(window, cx);
3902 } else {
3903 editor.discard_inline_completion(false, cx);
3904 }
3905
3906 cx.notify();
3907 } else if editor.completion_tasks.len() <= 1 {
3908 // If there are no more completion tasks and the last menu was
3909 // empty, we should hide it.
3910 let was_hidden = editor.hide_context_menu(window, cx).is_none();
3911 // If it was already hidden and we don't show inline
3912 // completions in the menu, we should also show the
3913 // inline-completion when available.
3914 if was_hidden && editor.show_inline_completions_in_menu(cx) {
3915 editor.update_visible_inline_completion(window, cx);
3916 }
3917 }
3918 })?;
3919
3920 Ok::<_, anyhow::Error>(())
3921 }
3922 .log_err()
3923 });
3924
3925 self.completion_tasks.push((id, task));
3926 }
3927
3928 pub fn confirm_completion(
3929 &mut self,
3930 action: &ConfirmCompletion,
3931 window: &mut Window,
3932 cx: &mut Context<Self>,
3933 ) -> Option<Task<Result<()>>> {
3934 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
3935 }
3936
3937 pub fn compose_completion(
3938 &mut self,
3939 action: &ComposeCompletion,
3940 window: &mut Window,
3941 cx: &mut Context<Self>,
3942 ) -> Option<Task<Result<()>>> {
3943 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
3944 }
3945
3946 fn toggle_zed_predict_onboarding(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3947 window.dispatch_action(zed_actions::OpenZedPredictOnboarding.boxed_clone(), cx);
3948 }
3949
3950 fn do_completion(
3951 &mut self,
3952 item_ix: Option<usize>,
3953 intent: CompletionIntent,
3954 window: &mut Window,
3955 cx: &mut Context<Editor>,
3956 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3957 use language::ToOffset as _;
3958
3959 let completions_menu =
3960 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
3961 menu
3962 } else {
3963 return None;
3964 };
3965
3966 let entries = completions_menu.entries.borrow();
3967 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
3968 if self.show_inline_completions_in_menu(cx) {
3969 self.discard_inline_completion(true, cx);
3970 }
3971 let candidate_id = mat.candidate_id;
3972 drop(entries);
3973
3974 let buffer_handle = completions_menu.buffer;
3975 let completion = completions_menu
3976 .completions
3977 .borrow()
3978 .get(candidate_id)?
3979 .clone();
3980 cx.stop_propagation();
3981
3982 let snippet;
3983 let text;
3984
3985 if completion.is_snippet() {
3986 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3987 text = snippet.as_ref().unwrap().text.clone();
3988 } else {
3989 snippet = None;
3990 text = completion.new_text.clone();
3991 };
3992 let selections = self.selections.all::<usize>(cx);
3993 let buffer = buffer_handle.read(cx);
3994 let old_range = completion.old_range.to_offset(buffer);
3995 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3996
3997 let newest_selection = self.selections.newest_anchor();
3998 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3999 return None;
4000 }
4001
4002 let lookbehind = newest_selection
4003 .start
4004 .text_anchor
4005 .to_offset(buffer)
4006 .saturating_sub(old_range.start);
4007 let lookahead = old_range
4008 .end
4009 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4010 let mut common_prefix_len = old_text
4011 .bytes()
4012 .zip(text.bytes())
4013 .take_while(|(a, b)| a == b)
4014 .count();
4015
4016 let snapshot = self.buffer.read(cx).snapshot(cx);
4017 let mut range_to_replace: Option<Range<isize>> = None;
4018 let mut ranges = Vec::new();
4019 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4020 for selection in &selections {
4021 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4022 let start = selection.start.saturating_sub(lookbehind);
4023 let end = selection.end + lookahead;
4024 if selection.id == newest_selection.id {
4025 range_to_replace = Some(
4026 ((start + common_prefix_len) as isize - selection.start as isize)
4027 ..(end as isize - selection.start as isize),
4028 );
4029 }
4030 ranges.push(start + common_prefix_len..end);
4031 } else {
4032 common_prefix_len = 0;
4033 ranges.clear();
4034 ranges.extend(selections.iter().map(|s| {
4035 if s.id == newest_selection.id {
4036 range_to_replace = Some(
4037 old_range.start.to_offset_utf16(&snapshot).0 as isize
4038 - selection.start as isize
4039 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4040 - selection.start as isize,
4041 );
4042 old_range.clone()
4043 } else {
4044 s.start..s.end
4045 }
4046 }));
4047 break;
4048 }
4049 if !self.linked_edit_ranges.is_empty() {
4050 let start_anchor = snapshot.anchor_before(selection.head());
4051 let end_anchor = snapshot.anchor_after(selection.tail());
4052 if let Some(ranges) = self
4053 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4054 {
4055 for (buffer, edits) in ranges {
4056 linked_edits.entry(buffer.clone()).or_default().extend(
4057 edits
4058 .into_iter()
4059 .map(|range| (range, text[common_prefix_len..].to_owned())),
4060 );
4061 }
4062 }
4063 }
4064 }
4065 let text = &text[common_prefix_len..];
4066
4067 cx.emit(EditorEvent::InputHandled {
4068 utf16_range_to_replace: range_to_replace,
4069 text: text.into(),
4070 });
4071
4072 self.transact(window, cx, |this, window, cx| {
4073 if let Some(mut snippet) = snippet {
4074 snippet.text = text.to_string();
4075 for tabstop in snippet
4076 .tabstops
4077 .iter_mut()
4078 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4079 {
4080 tabstop.start -= common_prefix_len as isize;
4081 tabstop.end -= common_prefix_len as isize;
4082 }
4083
4084 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4085 } else {
4086 this.buffer.update(cx, |buffer, cx| {
4087 buffer.edit(
4088 ranges.iter().map(|range| (range.clone(), text)),
4089 this.autoindent_mode.clone(),
4090 cx,
4091 );
4092 });
4093 }
4094 for (buffer, edits) in linked_edits {
4095 buffer.update(cx, |buffer, cx| {
4096 let snapshot = buffer.snapshot();
4097 let edits = edits
4098 .into_iter()
4099 .map(|(range, text)| {
4100 use text::ToPoint as TP;
4101 let end_point = TP::to_point(&range.end, &snapshot);
4102 let start_point = TP::to_point(&range.start, &snapshot);
4103 (start_point..end_point, text)
4104 })
4105 .sorted_by_key(|(range, _)| range.start)
4106 .collect::<Vec<_>>();
4107 buffer.edit(edits, None, cx);
4108 })
4109 }
4110
4111 this.refresh_inline_completion(true, false, window, cx);
4112 });
4113
4114 let show_new_completions_on_confirm = completion
4115 .confirm
4116 .as_ref()
4117 .map_or(false, |confirm| confirm(intent, window, cx));
4118 if show_new_completions_on_confirm {
4119 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4120 }
4121
4122 let provider = self.completion_provider.as_ref()?;
4123 drop(completion);
4124 let apply_edits = provider.apply_additional_edits_for_completion(
4125 buffer_handle,
4126 completions_menu.completions.clone(),
4127 candidate_id,
4128 true,
4129 cx,
4130 );
4131
4132 let editor_settings = EditorSettings::get_global(cx);
4133 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4134 // After the code completion is finished, users often want to know what signatures are needed.
4135 // so we should automatically call signature_help
4136 self.show_signature_help(&ShowSignatureHelp, window, cx);
4137 }
4138
4139 Some(cx.foreground_executor().spawn(async move {
4140 apply_edits.await?;
4141 Ok(())
4142 }))
4143 }
4144
4145 pub fn toggle_code_actions(
4146 &mut self,
4147 action: &ToggleCodeActions,
4148 window: &mut Window,
4149 cx: &mut Context<Self>,
4150 ) {
4151 let mut context_menu = self.context_menu.borrow_mut();
4152 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4153 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4154 // Toggle if we're selecting the same one
4155 *context_menu = None;
4156 cx.notify();
4157 return;
4158 } else {
4159 // Otherwise, clear it and start a new one
4160 *context_menu = None;
4161 cx.notify();
4162 }
4163 }
4164 drop(context_menu);
4165 let snapshot = self.snapshot(window, cx);
4166 let deployed_from_indicator = action.deployed_from_indicator;
4167 let mut task = self.code_actions_task.take();
4168 let action = action.clone();
4169 cx.spawn_in(window, |editor, mut cx| async move {
4170 while let Some(prev_task) = task {
4171 prev_task.await.log_err();
4172 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4173 }
4174
4175 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4176 if editor.focus_handle.is_focused(window) {
4177 let multibuffer_point = action
4178 .deployed_from_indicator
4179 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4180 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4181 let (buffer, buffer_row) = snapshot
4182 .buffer_snapshot
4183 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4184 .and_then(|(buffer_snapshot, range)| {
4185 editor
4186 .buffer
4187 .read(cx)
4188 .buffer(buffer_snapshot.remote_id())
4189 .map(|buffer| (buffer, range.start.row))
4190 })?;
4191 let (_, code_actions) = editor
4192 .available_code_actions
4193 .clone()
4194 .and_then(|(location, code_actions)| {
4195 let snapshot = location.buffer.read(cx).snapshot();
4196 let point_range = location.range.to_point(&snapshot);
4197 let point_range = point_range.start.row..=point_range.end.row;
4198 if point_range.contains(&buffer_row) {
4199 Some((location, code_actions))
4200 } else {
4201 None
4202 }
4203 })
4204 .unzip();
4205 let buffer_id = buffer.read(cx).remote_id();
4206 let tasks = editor
4207 .tasks
4208 .get(&(buffer_id, buffer_row))
4209 .map(|t| Arc::new(t.to_owned()));
4210 if tasks.is_none() && code_actions.is_none() {
4211 return None;
4212 }
4213
4214 editor.completion_tasks.clear();
4215 editor.discard_inline_completion(false, cx);
4216 let task_context =
4217 tasks
4218 .as_ref()
4219 .zip(editor.project.clone())
4220 .map(|(tasks, project)| {
4221 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4222 });
4223
4224 Some(cx.spawn_in(window, |editor, mut cx| async move {
4225 let task_context = match task_context {
4226 Some(task_context) => task_context.await,
4227 None => None,
4228 };
4229 let resolved_tasks =
4230 tasks.zip(task_context).map(|(tasks, task_context)| {
4231 Rc::new(ResolvedTasks {
4232 templates: tasks.resolve(&task_context).collect(),
4233 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4234 multibuffer_point.row,
4235 tasks.column,
4236 )),
4237 })
4238 });
4239 let spawn_straight_away = resolved_tasks
4240 .as_ref()
4241 .map_or(false, |tasks| tasks.templates.len() == 1)
4242 && code_actions
4243 .as_ref()
4244 .map_or(true, |actions| actions.is_empty());
4245 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4246 *editor.context_menu.borrow_mut() =
4247 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4248 buffer,
4249 actions: CodeActionContents {
4250 tasks: resolved_tasks,
4251 actions: code_actions,
4252 },
4253 selected_item: Default::default(),
4254 scroll_handle: UniformListScrollHandle::default(),
4255 deployed_from_indicator,
4256 }));
4257 if spawn_straight_away {
4258 if let Some(task) = editor.confirm_code_action(
4259 &ConfirmCodeAction { item_ix: Some(0) },
4260 window,
4261 cx,
4262 ) {
4263 cx.notify();
4264 return task;
4265 }
4266 }
4267 cx.notify();
4268 Task::ready(Ok(()))
4269 }) {
4270 task.await
4271 } else {
4272 Ok(())
4273 }
4274 }))
4275 } else {
4276 Some(Task::ready(Ok(())))
4277 }
4278 })?;
4279 if let Some(task) = spawned_test_task {
4280 task.await?;
4281 }
4282
4283 Ok::<_, anyhow::Error>(())
4284 })
4285 .detach_and_log_err(cx);
4286 }
4287
4288 pub fn confirm_code_action(
4289 &mut self,
4290 action: &ConfirmCodeAction,
4291 window: &mut Window,
4292 cx: &mut Context<Self>,
4293 ) -> Option<Task<Result<()>>> {
4294 let actions_menu =
4295 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4296 menu
4297 } else {
4298 return None;
4299 };
4300 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4301 let action = actions_menu.actions.get(action_ix)?;
4302 let title = action.label();
4303 let buffer = actions_menu.buffer;
4304 let workspace = self.workspace()?;
4305
4306 match action {
4307 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4308 workspace.update(cx, |workspace, cx| {
4309 workspace::tasks::schedule_resolved_task(
4310 workspace,
4311 task_source_kind,
4312 resolved_task,
4313 false,
4314 cx,
4315 );
4316
4317 Some(Task::ready(Ok(())))
4318 })
4319 }
4320 CodeActionsItem::CodeAction {
4321 excerpt_id,
4322 action,
4323 provider,
4324 } => {
4325 let apply_code_action =
4326 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4327 let workspace = workspace.downgrade();
4328 Some(cx.spawn_in(window, |editor, cx| async move {
4329 let project_transaction = apply_code_action.await?;
4330 Self::open_project_transaction(
4331 &editor,
4332 workspace,
4333 project_transaction,
4334 title,
4335 cx,
4336 )
4337 .await
4338 }))
4339 }
4340 }
4341 }
4342
4343 pub async fn open_project_transaction(
4344 this: &WeakEntity<Editor>,
4345 workspace: WeakEntity<Workspace>,
4346 transaction: ProjectTransaction,
4347 title: String,
4348 mut cx: AsyncWindowContext,
4349 ) -> Result<()> {
4350 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4351 cx.update(|_, cx| {
4352 entries.sort_unstable_by_key(|(buffer, _)| {
4353 buffer.read(cx).file().map(|f| f.path().clone())
4354 });
4355 })?;
4356
4357 // If the project transaction's edits are all contained within this editor, then
4358 // avoid opening a new editor to display them.
4359
4360 if let Some((buffer, transaction)) = entries.first() {
4361 if entries.len() == 1 {
4362 let excerpt = this.update(&mut cx, |editor, cx| {
4363 editor
4364 .buffer()
4365 .read(cx)
4366 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4367 })?;
4368 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4369 if excerpted_buffer == *buffer {
4370 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4371 let excerpt_range = excerpt_range.to_offset(buffer);
4372 buffer
4373 .edited_ranges_for_transaction::<usize>(transaction)
4374 .all(|range| {
4375 excerpt_range.start <= range.start
4376 && excerpt_range.end >= range.end
4377 })
4378 })?;
4379
4380 if all_edits_within_excerpt {
4381 return Ok(());
4382 }
4383 }
4384 }
4385 }
4386 } else {
4387 return Ok(());
4388 }
4389
4390 let mut ranges_to_highlight = Vec::new();
4391 let excerpt_buffer = cx.new(|cx| {
4392 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4393 for (buffer_handle, transaction) in &entries {
4394 let buffer = buffer_handle.read(cx);
4395 ranges_to_highlight.extend(
4396 multibuffer.push_excerpts_with_context_lines(
4397 buffer_handle.clone(),
4398 buffer
4399 .edited_ranges_for_transaction::<usize>(transaction)
4400 .collect(),
4401 DEFAULT_MULTIBUFFER_CONTEXT,
4402 cx,
4403 ),
4404 );
4405 }
4406 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4407 multibuffer
4408 })?;
4409
4410 workspace.update_in(&mut cx, |workspace, window, cx| {
4411 let project = workspace.project().clone();
4412 let editor = cx
4413 .new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, window, cx));
4414 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4415 editor.update(cx, |editor, cx| {
4416 editor.highlight_background::<Self>(
4417 &ranges_to_highlight,
4418 |theme| theme.editor_highlighted_line_background,
4419 cx,
4420 );
4421 });
4422 })?;
4423
4424 Ok(())
4425 }
4426
4427 pub fn clear_code_action_providers(&mut self) {
4428 self.code_action_providers.clear();
4429 self.available_code_actions.take();
4430 }
4431
4432 pub fn add_code_action_provider(
4433 &mut self,
4434 provider: Rc<dyn CodeActionProvider>,
4435 window: &mut Window,
4436 cx: &mut Context<Self>,
4437 ) {
4438 if self
4439 .code_action_providers
4440 .iter()
4441 .any(|existing_provider| existing_provider.id() == provider.id())
4442 {
4443 return;
4444 }
4445
4446 self.code_action_providers.push(provider);
4447 self.refresh_code_actions(window, cx);
4448 }
4449
4450 pub fn remove_code_action_provider(
4451 &mut self,
4452 id: Arc<str>,
4453 window: &mut Window,
4454 cx: &mut Context<Self>,
4455 ) {
4456 self.code_action_providers
4457 .retain(|provider| provider.id() != id);
4458 self.refresh_code_actions(window, cx);
4459 }
4460
4461 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4462 let buffer = self.buffer.read(cx);
4463 let newest_selection = self.selections.newest_anchor().clone();
4464 if newest_selection.head().diff_base_anchor.is_some() {
4465 return None;
4466 }
4467 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4468 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4469 if start_buffer != end_buffer {
4470 return None;
4471 }
4472
4473 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4474 cx.background_executor()
4475 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4476 .await;
4477
4478 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4479 let providers = this.code_action_providers.clone();
4480 let tasks = this
4481 .code_action_providers
4482 .iter()
4483 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4484 .collect::<Vec<_>>();
4485 (providers, tasks)
4486 })?;
4487
4488 let mut actions = Vec::new();
4489 for (provider, provider_actions) in
4490 providers.into_iter().zip(future::join_all(tasks).await)
4491 {
4492 if let Some(provider_actions) = provider_actions.log_err() {
4493 actions.extend(provider_actions.into_iter().map(|action| {
4494 AvailableCodeAction {
4495 excerpt_id: newest_selection.start.excerpt_id,
4496 action,
4497 provider: provider.clone(),
4498 }
4499 }));
4500 }
4501 }
4502
4503 this.update(&mut cx, |this, cx| {
4504 this.available_code_actions = if actions.is_empty() {
4505 None
4506 } else {
4507 Some((
4508 Location {
4509 buffer: start_buffer,
4510 range: start..end,
4511 },
4512 actions.into(),
4513 ))
4514 };
4515 cx.notify();
4516 })
4517 }));
4518 None
4519 }
4520
4521 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4522 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4523 self.show_git_blame_inline = false;
4524
4525 self.show_git_blame_inline_delay_task =
4526 Some(cx.spawn_in(window, |this, mut cx| async move {
4527 cx.background_executor().timer(delay).await;
4528
4529 this.update(&mut cx, |this, cx| {
4530 this.show_git_blame_inline = true;
4531 cx.notify();
4532 })
4533 .log_err();
4534 }));
4535 }
4536 }
4537
4538 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4539 if self.pending_rename.is_some() {
4540 return None;
4541 }
4542
4543 let provider = self.semantics_provider.clone()?;
4544 let buffer = self.buffer.read(cx);
4545 let newest_selection = self.selections.newest_anchor().clone();
4546 let cursor_position = newest_selection.head();
4547 let (cursor_buffer, cursor_buffer_position) =
4548 buffer.text_anchor_for_position(cursor_position, cx)?;
4549 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4550 if cursor_buffer != tail_buffer {
4551 return None;
4552 }
4553 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4554 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4555 cx.background_executor()
4556 .timer(Duration::from_millis(debounce))
4557 .await;
4558
4559 let highlights = if let Some(highlights) = cx
4560 .update(|cx| {
4561 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4562 })
4563 .ok()
4564 .flatten()
4565 {
4566 highlights.await.log_err()
4567 } else {
4568 None
4569 };
4570
4571 if let Some(highlights) = highlights {
4572 this.update(&mut cx, |this, cx| {
4573 if this.pending_rename.is_some() {
4574 return;
4575 }
4576
4577 let buffer_id = cursor_position.buffer_id;
4578 let buffer = this.buffer.read(cx);
4579 if !buffer
4580 .text_anchor_for_position(cursor_position, cx)
4581 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4582 {
4583 return;
4584 }
4585
4586 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4587 let mut write_ranges = Vec::new();
4588 let mut read_ranges = Vec::new();
4589 for highlight in highlights {
4590 for (excerpt_id, excerpt_range) in
4591 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4592 {
4593 let start = highlight
4594 .range
4595 .start
4596 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4597 let end = highlight
4598 .range
4599 .end
4600 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4601 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4602 continue;
4603 }
4604
4605 let range = Anchor {
4606 buffer_id,
4607 excerpt_id,
4608 text_anchor: start,
4609 diff_base_anchor: None,
4610 }..Anchor {
4611 buffer_id,
4612 excerpt_id,
4613 text_anchor: end,
4614 diff_base_anchor: None,
4615 };
4616 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4617 write_ranges.push(range);
4618 } else {
4619 read_ranges.push(range);
4620 }
4621 }
4622 }
4623
4624 this.highlight_background::<DocumentHighlightRead>(
4625 &read_ranges,
4626 |theme| theme.editor_document_highlight_read_background,
4627 cx,
4628 );
4629 this.highlight_background::<DocumentHighlightWrite>(
4630 &write_ranges,
4631 |theme| theme.editor_document_highlight_write_background,
4632 cx,
4633 );
4634 cx.notify();
4635 })
4636 .log_err();
4637 }
4638 }));
4639 None
4640 }
4641
4642 pub fn refresh_inline_completion(
4643 &mut self,
4644 debounce: bool,
4645 user_requested: bool,
4646 window: &mut Window,
4647 cx: &mut Context<Self>,
4648 ) -> Option<()> {
4649 let provider = self.inline_completion_provider()?;
4650 let cursor = self.selections.newest_anchor().head();
4651 let (buffer, cursor_buffer_position) =
4652 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4653
4654 if !user_requested
4655 && (!self.enable_inline_completions
4656 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4657 || !self.is_focused(window)
4658 || buffer.read(cx).is_empty())
4659 {
4660 self.discard_inline_completion(false, cx);
4661 return None;
4662 }
4663
4664 self.update_visible_inline_completion(window, cx);
4665 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4666 Some(())
4667 }
4668
4669 fn cycle_inline_completion(
4670 &mut self,
4671 direction: Direction,
4672 window: &mut Window,
4673 cx: &mut Context<Self>,
4674 ) -> Option<()> {
4675 let provider = self.inline_completion_provider()?;
4676 let cursor = self.selections.newest_anchor().head();
4677 let (buffer, cursor_buffer_position) =
4678 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4679 if !self.enable_inline_completions
4680 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4681 {
4682 return None;
4683 }
4684
4685 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4686 self.update_visible_inline_completion(window, cx);
4687
4688 Some(())
4689 }
4690
4691 pub fn show_inline_completion(
4692 &mut self,
4693 _: &ShowInlineCompletion,
4694 window: &mut Window,
4695 cx: &mut Context<Self>,
4696 ) {
4697 if !self.has_active_inline_completion() {
4698 self.refresh_inline_completion(false, true, window, cx);
4699 return;
4700 }
4701
4702 self.update_visible_inline_completion(window, cx);
4703 }
4704
4705 pub fn display_cursor_names(
4706 &mut self,
4707 _: &DisplayCursorNames,
4708 window: &mut Window,
4709 cx: &mut Context<Self>,
4710 ) {
4711 self.show_cursor_names(window, cx);
4712 }
4713
4714 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4715 self.show_cursor_names = true;
4716 cx.notify();
4717 cx.spawn_in(window, |this, mut cx| async move {
4718 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4719 this.update(&mut cx, |this, cx| {
4720 this.show_cursor_names = false;
4721 cx.notify()
4722 })
4723 .ok()
4724 })
4725 .detach();
4726 }
4727
4728 pub fn next_inline_completion(
4729 &mut self,
4730 _: &NextInlineCompletion,
4731 window: &mut Window,
4732 cx: &mut Context<Self>,
4733 ) {
4734 if self.has_active_inline_completion() {
4735 self.cycle_inline_completion(Direction::Next, window, cx);
4736 } else {
4737 let is_copilot_disabled = self
4738 .refresh_inline_completion(false, true, window, cx)
4739 .is_none();
4740 if is_copilot_disabled {
4741 cx.propagate();
4742 }
4743 }
4744 }
4745
4746 pub fn previous_inline_completion(
4747 &mut self,
4748 _: &PreviousInlineCompletion,
4749 window: &mut Window,
4750 cx: &mut Context<Self>,
4751 ) {
4752 if self.has_active_inline_completion() {
4753 self.cycle_inline_completion(Direction::Prev, window, cx);
4754 } else {
4755 let is_copilot_disabled = self
4756 .refresh_inline_completion(false, true, window, cx)
4757 .is_none();
4758 if is_copilot_disabled {
4759 cx.propagate();
4760 }
4761 }
4762 }
4763
4764 pub fn accept_inline_completion(
4765 &mut self,
4766 _: &AcceptInlineCompletion,
4767 window: &mut Window,
4768 cx: &mut Context<Self>,
4769 ) {
4770 let buffer = self.buffer.read(cx);
4771 let snapshot = buffer.snapshot(cx);
4772 let selection = self.selections.newest_adjusted(cx);
4773 let cursor = selection.head();
4774 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
4775 let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
4776 if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
4777 {
4778 if cursor.column < suggested_indent.len
4779 && cursor.column <= current_indent.len
4780 && current_indent.len <= suggested_indent.len
4781 {
4782 self.tab(&Default::default(), window, cx);
4783 return;
4784 }
4785 }
4786
4787 if self.show_inline_completions_in_menu(cx) {
4788 self.hide_context_menu(window, cx);
4789 }
4790
4791 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4792 return;
4793 };
4794
4795 self.report_inline_completion_event(true, cx);
4796
4797 match &active_inline_completion.completion {
4798 InlineCompletion::Move { target, .. } => {
4799 let target = *target;
4800 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
4801 selections.select_anchor_ranges([target..target]);
4802 });
4803 }
4804 InlineCompletion::Edit { edits, .. } => {
4805 if let Some(provider) = self.inline_completion_provider() {
4806 provider.accept(cx);
4807 }
4808
4809 let snapshot = self.buffer.read(cx).snapshot(cx);
4810 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4811
4812 self.buffer.update(cx, |buffer, cx| {
4813 buffer.edit(edits.iter().cloned(), None, cx)
4814 });
4815
4816 self.change_selections(None, window, cx, |s| {
4817 s.select_anchor_ranges([last_edit_end..last_edit_end])
4818 });
4819
4820 self.update_visible_inline_completion(window, cx);
4821 if self.active_inline_completion.is_none() {
4822 self.refresh_inline_completion(true, true, window, cx);
4823 }
4824
4825 cx.notify();
4826 }
4827 }
4828 }
4829
4830 pub fn accept_partial_inline_completion(
4831 &mut self,
4832 _: &AcceptPartialInlineCompletion,
4833 window: &mut Window,
4834 cx: &mut Context<Self>,
4835 ) {
4836 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4837 return;
4838 };
4839 if self.selections.count() != 1 {
4840 return;
4841 }
4842
4843 self.report_inline_completion_event(true, cx);
4844
4845 match &active_inline_completion.completion {
4846 InlineCompletion::Move { target, .. } => {
4847 let target = *target;
4848 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
4849 selections.select_anchor_ranges([target..target]);
4850 });
4851 }
4852 InlineCompletion::Edit { edits, .. } => {
4853 // Find an insertion that starts at the cursor position.
4854 let snapshot = self.buffer.read(cx).snapshot(cx);
4855 let cursor_offset = self.selections.newest::<usize>(cx).head();
4856 let insertion = edits.iter().find_map(|(range, text)| {
4857 let range = range.to_offset(&snapshot);
4858 if range.is_empty() && range.start == cursor_offset {
4859 Some(text)
4860 } else {
4861 None
4862 }
4863 });
4864
4865 if let Some(text) = insertion {
4866 let mut partial_completion = text
4867 .chars()
4868 .by_ref()
4869 .take_while(|c| c.is_alphabetic())
4870 .collect::<String>();
4871 if partial_completion.is_empty() {
4872 partial_completion = text
4873 .chars()
4874 .by_ref()
4875 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4876 .collect::<String>();
4877 }
4878
4879 cx.emit(EditorEvent::InputHandled {
4880 utf16_range_to_replace: None,
4881 text: partial_completion.clone().into(),
4882 });
4883
4884 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
4885
4886 self.refresh_inline_completion(true, true, window, cx);
4887 cx.notify();
4888 } else {
4889 self.accept_inline_completion(&Default::default(), window, cx);
4890 }
4891 }
4892 }
4893 }
4894
4895 fn discard_inline_completion(
4896 &mut self,
4897 should_report_inline_completion_event: bool,
4898 cx: &mut Context<Self>,
4899 ) -> bool {
4900 if should_report_inline_completion_event {
4901 self.report_inline_completion_event(false, cx);
4902 }
4903
4904 if let Some(provider) = self.inline_completion_provider() {
4905 provider.discard(cx);
4906 }
4907
4908 self.take_active_inline_completion(cx)
4909 }
4910
4911 fn report_inline_completion_event(&self, accepted: bool, cx: &App) {
4912 let Some(provider) = self.inline_completion_provider() else {
4913 return;
4914 };
4915
4916 let Some((_, buffer, _)) = self
4917 .buffer
4918 .read(cx)
4919 .excerpt_containing(self.selections.newest_anchor().head(), cx)
4920 else {
4921 return;
4922 };
4923
4924 let extension = buffer
4925 .read(cx)
4926 .file()
4927 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
4928
4929 let event_type = match accepted {
4930 true => "Edit Prediction Accepted",
4931 false => "Edit Prediction Discarded",
4932 };
4933 telemetry::event!(
4934 event_type,
4935 provider = provider.name(),
4936 suggestion_accepted = accepted,
4937 file_extension = extension,
4938 );
4939 }
4940
4941 pub fn has_active_inline_completion(&self) -> bool {
4942 self.active_inline_completion.is_some()
4943 }
4944
4945 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
4946 let Some(active_inline_completion) = self.active_inline_completion.take() else {
4947 return false;
4948 };
4949
4950 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
4951 self.clear_highlights::<InlineCompletionHighlight>(cx);
4952 self.stale_inline_completion_in_menu = Some(active_inline_completion);
4953 true
4954 }
4955
4956 pub fn is_previewing_inline_completion(&self) -> bool {
4957 matches!(
4958 self.context_menu.borrow().as_ref(),
4959 Some(CodeContextMenu::Completions(menu)) if !menu.is_empty() && menu.previewing_inline_completion
4960 )
4961 }
4962
4963 fn update_inline_completion_preview(
4964 &mut self,
4965 modifiers: &Modifiers,
4966 window: &mut Window,
4967 cx: &mut Context<Self>,
4968 ) {
4969 // Moves jump directly with a preview step
4970
4971 if self
4972 .active_inline_completion
4973 .as_ref()
4974 .map_or(true, |c| c.is_move())
4975 {
4976 cx.notify();
4977 return;
4978 }
4979
4980 if !self.show_inline_completions_in_menu(cx) {
4981 return;
4982 }
4983
4984 let mut menu_borrow = self.context_menu.borrow_mut();
4985
4986 let Some(CodeContextMenu::Completions(completions_menu)) = menu_borrow.as_mut() else {
4987 return;
4988 };
4989
4990 if completions_menu.is_empty()
4991 || completions_menu.previewing_inline_completion == modifiers.alt
4992 {
4993 return;
4994 }
4995
4996 completions_menu.set_previewing_inline_completion(modifiers.alt);
4997 drop(menu_borrow);
4998 self.update_visible_inline_completion(window, cx);
4999 }
5000
5001 fn update_visible_inline_completion(
5002 &mut self,
5003 _window: &mut Window,
5004 cx: &mut Context<Self>,
5005 ) -> Option<()> {
5006 let selection = self.selections.newest_anchor();
5007 let cursor = selection.head();
5008 let multibuffer = self.buffer.read(cx).snapshot(cx);
5009 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5010 let excerpt_id = cursor.excerpt_id;
5011
5012 let show_in_menu = self.show_inline_completions_in_menu(cx);
5013 let completions_menu_has_precedence = !show_in_menu
5014 && (self.context_menu.borrow().is_some()
5015 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5016 if completions_menu_has_precedence
5017 || !offset_selection.is_empty()
5018 || !self.enable_inline_completions
5019 || self
5020 .active_inline_completion
5021 .as_ref()
5022 .map_or(false, |completion| {
5023 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5024 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5025 !invalidation_range.contains(&offset_selection.head())
5026 })
5027 {
5028 self.discard_inline_completion(false, cx);
5029 return None;
5030 }
5031
5032 self.take_active_inline_completion(cx);
5033 let provider = self.inline_completion_provider()?;
5034
5035 let (buffer, cursor_buffer_position) =
5036 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5037
5038 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5039 let edits = inline_completion
5040 .edits
5041 .into_iter()
5042 .flat_map(|(range, new_text)| {
5043 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5044 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5045 Some((start..end, new_text))
5046 })
5047 .collect::<Vec<_>>();
5048 if edits.is_empty() {
5049 return None;
5050 }
5051
5052 let first_edit_start = edits.first().unwrap().0.start;
5053 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5054 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5055
5056 let last_edit_end = edits.last().unwrap().0.end;
5057 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5058 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5059
5060 let cursor_row = cursor.to_point(&multibuffer).row;
5061
5062 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5063
5064 let mut inlay_ids = Vec::new();
5065 let invalidation_row_range;
5066 let move_invalidation_row_range = if cursor_row < edit_start_row {
5067 Some(cursor_row..edit_end_row)
5068 } else if cursor_row > edit_end_row {
5069 Some(edit_start_row..cursor_row)
5070 } else {
5071 None
5072 };
5073 let completion = if let Some(move_invalidation_row_range) = move_invalidation_row_range {
5074 invalidation_row_range = move_invalidation_row_range;
5075 let target = first_edit_start;
5076 let target_point = text::ToPoint::to_point(&target.text_anchor, &snapshot);
5077 // TODO: Base this off of TreeSitter or word boundaries?
5078 let target_excerpt_begin = snapshot.anchor_before(snapshot.clip_point(
5079 Point::new(target_point.row, target_point.column.saturating_sub(20)),
5080 Bias::Left,
5081 ));
5082 let target_excerpt_end = snapshot.anchor_after(snapshot.clip_point(
5083 Point::new(target_point.row, target_point.column + 20),
5084 Bias::Right,
5085 ));
5086 let range_around_target = target_excerpt_begin..target_excerpt_end;
5087 InlineCompletion::Move {
5088 target,
5089 range_around_target,
5090 snapshot,
5091 }
5092 } else {
5093 if !show_in_menu || !self.has_active_completions_menu() {
5094 if edits
5095 .iter()
5096 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5097 {
5098 let mut inlays = Vec::new();
5099 for (range, new_text) in &edits {
5100 let inlay = Inlay::inline_completion(
5101 post_inc(&mut self.next_inlay_id),
5102 range.start,
5103 new_text.as_str(),
5104 );
5105 inlay_ids.push(inlay.id);
5106 inlays.push(inlay);
5107 }
5108
5109 self.splice_inlays(&[], inlays, cx);
5110 } else {
5111 let background_color = cx.theme().status().deleted_background;
5112 self.highlight_text::<InlineCompletionHighlight>(
5113 edits.iter().map(|(range, _)| range.clone()).collect(),
5114 HighlightStyle {
5115 background_color: Some(background_color),
5116 ..Default::default()
5117 },
5118 cx,
5119 );
5120 }
5121 }
5122
5123 invalidation_row_range = edit_start_row..edit_end_row;
5124
5125 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5126 if provider.show_tab_accept_marker() {
5127 EditDisplayMode::TabAccept(self.is_previewing_inline_completion())
5128 } else {
5129 EditDisplayMode::Inline
5130 }
5131 } else {
5132 EditDisplayMode::DiffPopover
5133 };
5134
5135 InlineCompletion::Edit {
5136 edits,
5137 edit_preview: inline_completion.edit_preview,
5138 display_mode,
5139 snapshot,
5140 }
5141 };
5142
5143 let invalidation_range = multibuffer
5144 .anchor_before(Point::new(invalidation_row_range.start, 0))
5145 ..multibuffer.anchor_after(Point::new(
5146 invalidation_row_range.end,
5147 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5148 ));
5149
5150 self.stale_inline_completion_in_menu = None;
5151 self.active_inline_completion = Some(InlineCompletionState {
5152 inlay_ids,
5153 completion,
5154 invalidation_range,
5155 });
5156
5157 cx.notify();
5158
5159 Some(())
5160 }
5161
5162 pub fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5163 Some(self.inline_completion_provider.as_ref()?.provider.clone())
5164 }
5165
5166 fn show_inline_completions_in_menu(&self, cx: &App) -> bool {
5167 let by_provider = matches!(
5168 self.menu_inline_completions_policy,
5169 MenuInlineCompletionsPolicy::ByProvider
5170 );
5171
5172 by_provider
5173 && EditorSettings::get_global(cx).show_inline_completions_in_menu
5174 && self
5175 .inline_completion_provider()
5176 .map_or(false, |provider| provider.show_completions_in_menu())
5177 }
5178
5179 fn render_code_actions_indicator(
5180 &self,
5181 _style: &EditorStyle,
5182 row: DisplayRow,
5183 is_active: bool,
5184 cx: &mut Context<Self>,
5185 ) -> Option<IconButton> {
5186 if self.available_code_actions.is_some() {
5187 Some(
5188 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5189 .shape(ui::IconButtonShape::Square)
5190 .icon_size(IconSize::XSmall)
5191 .icon_color(Color::Muted)
5192 .toggle_state(is_active)
5193 .tooltip({
5194 let focus_handle = self.focus_handle.clone();
5195 move |window, cx| {
5196 Tooltip::for_action_in(
5197 "Toggle Code Actions",
5198 &ToggleCodeActions {
5199 deployed_from_indicator: None,
5200 },
5201 &focus_handle,
5202 window,
5203 cx,
5204 )
5205 }
5206 })
5207 .on_click(cx.listener(move |editor, _e, window, cx| {
5208 window.focus(&editor.focus_handle(cx));
5209 editor.toggle_code_actions(
5210 &ToggleCodeActions {
5211 deployed_from_indicator: Some(row),
5212 },
5213 window,
5214 cx,
5215 );
5216 })),
5217 )
5218 } else {
5219 None
5220 }
5221 }
5222
5223 fn clear_tasks(&mut self) {
5224 self.tasks.clear()
5225 }
5226
5227 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5228 if self.tasks.insert(key, value).is_some() {
5229 // This case should hopefully be rare, but just in case...
5230 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5231 }
5232 }
5233
5234 fn build_tasks_context(
5235 project: &Entity<Project>,
5236 buffer: &Entity<Buffer>,
5237 buffer_row: u32,
5238 tasks: &Arc<RunnableTasks>,
5239 cx: &mut Context<Self>,
5240 ) -> Task<Option<task::TaskContext>> {
5241 let position = Point::new(buffer_row, tasks.column);
5242 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5243 let location = Location {
5244 buffer: buffer.clone(),
5245 range: range_start..range_start,
5246 };
5247 // Fill in the environmental variables from the tree-sitter captures
5248 let mut captured_task_variables = TaskVariables::default();
5249 for (capture_name, value) in tasks.extra_variables.clone() {
5250 captured_task_variables.insert(
5251 task::VariableName::Custom(capture_name.into()),
5252 value.clone(),
5253 );
5254 }
5255 project.update(cx, |project, cx| {
5256 project.task_store().update(cx, |task_store, cx| {
5257 task_store.task_context_for_location(captured_task_variables, location, cx)
5258 })
5259 })
5260 }
5261
5262 pub fn spawn_nearest_task(
5263 &mut self,
5264 action: &SpawnNearestTask,
5265 window: &mut Window,
5266 cx: &mut Context<Self>,
5267 ) {
5268 let Some((workspace, _)) = self.workspace.clone() else {
5269 return;
5270 };
5271 let Some(project) = self.project.clone() else {
5272 return;
5273 };
5274
5275 // Try to find a closest, enclosing node using tree-sitter that has a
5276 // task
5277 let Some((buffer, buffer_row, tasks)) = self
5278 .find_enclosing_node_task(cx)
5279 // Or find the task that's closest in row-distance.
5280 .or_else(|| self.find_closest_task(cx))
5281 else {
5282 return;
5283 };
5284
5285 let reveal_strategy = action.reveal;
5286 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5287 cx.spawn_in(window, |_, mut cx| async move {
5288 let context = task_context.await?;
5289 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5290
5291 let resolved = resolved_task.resolved.as_mut()?;
5292 resolved.reveal = reveal_strategy;
5293
5294 workspace
5295 .update(&mut cx, |workspace, cx| {
5296 workspace::tasks::schedule_resolved_task(
5297 workspace,
5298 task_source_kind,
5299 resolved_task,
5300 false,
5301 cx,
5302 );
5303 })
5304 .ok()
5305 })
5306 .detach();
5307 }
5308
5309 fn find_closest_task(
5310 &mut self,
5311 cx: &mut Context<Self>,
5312 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5313 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5314
5315 let ((buffer_id, row), tasks) = self
5316 .tasks
5317 .iter()
5318 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5319
5320 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5321 let tasks = Arc::new(tasks.to_owned());
5322 Some((buffer, *row, tasks))
5323 }
5324
5325 fn find_enclosing_node_task(
5326 &mut self,
5327 cx: &mut Context<Self>,
5328 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5329 let snapshot = self.buffer.read(cx).snapshot(cx);
5330 let offset = self.selections.newest::<usize>(cx).head();
5331 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5332 let buffer_id = excerpt.buffer().remote_id();
5333
5334 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5335 let mut cursor = layer.node().walk();
5336
5337 while cursor.goto_first_child_for_byte(offset).is_some() {
5338 if cursor.node().end_byte() == offset {
5339 cursor.goto_next_sibling();
5340 }
5341 }
5342
5343 // Ascend to the smallest ancestor that contains the range and has a task.
5344 loop {
5345 let node = cursor.node();
5346 let node_range = node.byte_range();
5347 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5348
5349 // Check if this node contains our offset
5350 if node_range.start <= offset && node_range.end >= offset {
5351 // If it contains offset, check for task
5352 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5353 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5354 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5355 }
5356 }
5357
5358 if !cursor.goto_parent() {
5359 break;
5360 }
5361 }
5362 None
5363 }
5364
5365 fn render_run_indicator(
5366 &self,
5367 _style: &EditorStyle,
5368 is_active: bool,
5369 row: DisplayRow,
5370 cx: &mut Context<Self>,
5371 ) -> IconButton {
5372 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5373 .shape(ui::IconButtonShape::Square)
5374 .icon_size(IconSize::XSmall)
5375 .icon_color(Color::Muted)
5376 .toggle_state(is_active)
5377 .on_click(cx.listener(move |editor, _e, window, cx| {
5378 window.focus(&editor.focus_handle(cx));
5379 editor.toggle_code_actions(
5380 &ToggleCodeActions {
5381 deployed_from_indicator: Some(row),
5382 },
5383 window,
5384 cx,
5385 );
5386 }))
5387 }
5388
5389 pub fn context_menu_visible(&self) -> bool {
5390 self.context_menu
5391 .borrow()
5392 .as_ref()
5393 .map_or(false, |menu| menu.visible())
5394 }
5395
5396 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
5397 self.context_menu
5398 .borrow()
5399 .as_ref()
5400 .map(|menu| menu.origin())
5401 }
5402
5403 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
5404 px(32.)
5405 }
5406
5407 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
5408 if self.read_only(cx) {
5409 cx.theme().players().read_only()
5410 } else {
5411 self.style.as_ref().unwrap().local_player
5412 }
5413 }
5414
5415 #[allow(clippy::too_many_arguments)]
5416 fn render_edit_prediction_cursor_popover(
5417 &self,
5418 min_width: Pixels,
5419 max_width: Pixels,
5420 cursor_point: Point,
5421 line_layouts: &[LineWithInvisibles],
5422 style: &EditorStyle,
5423 accept_keystroke: &gpui::Keystroke,
5424 window: &Window,
5425 cx: &mut Context<Editor>,
5426 ) -> Option<AnyElement> {
5427 let provider = self.inline_completion_provider.as_ref()?;
5428
5429 if provider.provider.needs_terms_acceptance(cx) {
5430 return Some(
5431 h_flex()
5432 .h(self.edit_prediction_cursor_popover_height())
5433 .min_w(min_width)
5434 .flex_1()
5435 .px_2()
5436 .gap_3()
5437 .elevation_2(cx)
5438 .hover(|style| style.bg(cx.theme().colors().element_hover))
5439 .id("accept-terms")
5440 .cursor_pointer()
5441 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
5442 .on_click(cx.listener(|this, _event, window, cx| {
5443 cx.stop_propagation();
5444 this.toggle_zed_predict_onboarding(window, cx)
5445 }))
5446 .child(
5447 h_flex()
5448 .w_full()
5449 .gap_2()
5450 .child(Icon::new(IconName::ZedPredict))
5451 .child(Label::new("Accept Terms of Service"))
5452 .child(div().w_full())
5453 .child(Icon::new(IconName::ArrowUpRight))
5454 .into_any_element(),
5455 )
5456 .into_any(),
5457 );
5458 }
5459
5460 let is_refreshing = provider.provider.is_refreshing(cx);
5461
5462 fn pending_completion_container() -> Div {
5463 h_flex()
5464 .flex_1()
5465 .gap_3()
5466 .child(Icon::new(IconName::ZedPredict))
5467 }
5468
5469 let completion = match &self.active_inline_completion {
5470 Some(completion) => self.render_edit_prediction_cursor_popover_preview(
5471 completion,
5472 cursor_point,
5473 line_layouts,
5474 style,
5475 cx,
5476 )?,
5477
5478 None if is_refreshing => match &self.stale_inline_completion_in_menu {
5479 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
5480 stale_completion,
5481 cursor_point,
5482 line_layouts,
5483 style,
5484 cx,
5485 )?,
5486
5487 None => {
5488 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
5489 }
5490 },
5491
5492 None => pending_completion_container().child(Label::new("No Prediction")),
5493 };
5494
5495 let buffer_font = theme::ThemeSettings::get_global(cx).buffer_font.clone();
5496 let completion = completion.font(buffer_font.clone());
5497
5498 let completion = if is_refreshing {
5499 completion
5500 .with_animation(
5501 "loading-completion",
5502 Animation::new(Duration::from_secs(2))
5503 .repeat()
5504 .with_easing(pulsating_between(0.4, 0.8)),
5505 |label, delta| label.opacity(delta),
5506 )
5507 .into_any_element()
5508 } else {
5509 completion.into_any_element()
5510 };
5511
5512 let has_completion = self.active_inline_completion.is_some();
5513
5514 Some(
5515 h_flex()
5516 .h(self.edit_prediction_cursor_popover_height())
5517 .min_w(min_width)
5518 .max_w(max_width)
5519 .flex_1()
5520 .px_2()
5521 .gap_3()
5522 .elevation_2(cx)
5523 .child(completion)
5524 .child(
5525 h_flex()
5526 .border_l_1()
5527 .border_color(cx.theme().colors().border_variant)
5528 .pl_2()
5529 .child(
5530 h_flex()
5531 .font(buffer_font.clone())
5532 .p_1()
5533 .rounded_sm()
5534 .children(ui::render_modifiers(
5535 &accept_keystroke.modifiers,
5536 PlatformStyle::platform(),
5537 if window.modifiers() == accept_keystroke.modifiers {
5538 Some(Color::Accent)
5539 } else {
5540 None
5541 },
5542 )),
5543 )
5544 .opacity(if has_completion { 1.0 } else { 0.1 })
5545 .child(
5546 if self
5547 .active_inline_completion
5548 .as_ref()
5549 .map_or(false, |c| c.is_move())
5550 {
5551 div()
5552 .child(ui::Key::new(&accept_keystroke.key, None))
5553 .font(buffer_font.clone())
5554 .into_any()
5555 } else {
5556 Label::new("Preview").color(Color::Muted).into_any_element()
5557 },
5558 ),
5559 )
5560 .into_any(),
5561 )
5562 }
5563
5564 fn render_edit_prediction_cursor_popover_preview(
5565 &self,
5566 completion: &InlineCompletionState,
5567 cursor_point: Point,
5568 line_layouts: &[LineWithInvisibles],
5569 style: &EditorStyle,
5570 cx: &mut Context<Editor>,
5571 ) -> Option<Div> {
5572 use text::ToPoint as _;
5573
5574 fn render_relative_row_jump(
5575 prefix: impl Into<String>,
5576 current_row: u32,
5577 target_row: u32,
5578 ) -> Div {
5579 let (row_diff, arrow) = if target_row < current_row {
5580 (current_row - target_row, IconName::ArrowUp)
5581 } else {
5582 (target_row - current_row, IconName::ArrowDown)
5583 };
5584
5585 h_flex()
5586 .child(
5587 Label::new(format!("{}{}", prefix.into(), row_diff))
5588 .color(Color::Muted)
5589 .size(LabelSize::Small),
5590 )
5591 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
5592 }
5593
5594 match &completion.completion {
5595 InlineCompletion::Edit {
5596 edits,
5597 edit_preview,
5598 snapshot,
5599 display_mode: _,
5600 } => {
5601 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
5602
5603 let highlighted_edits = crate::inline_completion_edit_text(
5604 &snapshot,
5605 &edits,
5606 edit_preview.as_ref()?,
5607 true,
5608 cx,
5609 );
5610
5611 let len_total = highlighted_edits.text.len();
5612 let first_line = &highlighted_edits.text
5613 [..highlighted_edits.text.find('\n').unwrap_or(len_total)];
5614 let first_line_len = first_line.len();
5615
5616 let first_highlight_start = highlighted_edits
5617 .highlights
5618 .first()
5619 .map_or(0, |(range, _)| range.start);
5620 let drop_prefix_len = first_line
5621 .char_indices()
5622 .find(|(_, c)| !c.is_whitespace())
5623 .map_or(first_highlight_start, |(ix, _)| {
5624 ix.min(first_highlight_start)
5625 });
5626
5627 let preview_text = &first_line[drop_prefix_len..];
5628 let preview_len = preview_text.len();
5629 let highlights = highlighted_edits
5630 .highlights
5631 .into_iter()
5632 .take_until(|(range, _)| range.start > first_line_len)
5633 .map(|(range, style)| {
5634 (
5635 range.start - drop_prefix_len
5636 ..(range.end - drop_prefix_len).min(preview_len),
5637 style,
5638 )
5639 });
5640
5641 let styled_text = gpui::StyledText::new(SharedString::new(preview_text))
5642 .with_highlights(&style.text, highlights);
5643
5644 let preview = h_flex()
5645 .gap_1()
5646 .child(styled_text)
5647 .when(len_total > first_line_len, |parent| parent.child("…"));
5648
5649 let left = if first_edit_row != cursor_point.row {
5650 render_relative_row_jump("", cursor_point.row, first_edit_row)
5651 .into_any_element()
5652 } else {
5653 Icon::new(IconName::ZedPredict).into_any_element()
5654 };
5655
5656 Some(h_flex().flex_1().gap_3().child(left).child(preview))
5657 }
5658
5659 InlineCompletion::Move {
5660 target,
5661 range_around_target,
5662 snapshot,
5663 } => {
5664 let highlighted_text = snapshot.highlighted_text_for_range(
5665 range_around_target.clone(),
5666 None,
5667 &style.syntax,
5668 );
5669 let cursor_color = self.current_user_player_color(cx).cursor;
5670
5671 let start_point = range_around_target.start.to_point(&snapshot);
5672 let end_point = range_around_target.end.to_point(&snapshot);
5673 let target_point = target.text_anchor.to_point(&snapshot);
5674
5675 let start_column_x =
5676 line_layouts[start_point.row as usize].x_for_index(start_point.column as usize);
5677 let target_column_x = line_layouts[target_point.row as usize]
5678 .x_for_index(target_point.column as usize);
5679 let cursor_relative_position = target_column_x - start_column_x;
5680
5681 let fade_before = start_point.column > 0;
5682 let fade_after = end_point.column < snapshot.line_len(end_point.row);
5683
5684 let background = cx.theme().colors().elevated_surface_background;
5685
5686 Some(
5687 h_flex()
5688 .gap_3()
5689 .flex_1()
5690 .child(render_relative_row_jump(
5691 "Jump ",
5692 cursor_point.row,
5693 target.text_anchor.to_point(&snapshot).row,
5694 ))
5695 .when(!highlighted_text.text.is_empty(), |parent| {
5696 parent.child(
5697 h_flex()
5698 .relative()
5699 .child(highlighted_text.to_styled_text(&style.text))
5700 .when(fade_before, |parent| {
5701 parent.child(
5702 div().absolute().top_0().left_0().w_4().h_full().bg(
5703 linear_gradient(
5704 90.,
5705 linear_color_stop(background, 0.),
5706 linear_color_stop(background.opacity(0.), 1.),
5707 ),
5708 ),
5709 )
5710 })
5711 .when(fade_after, |parent| {
5712 parent.child(
5713 div().absolute().top_0().right_0().w_4().h_full().bg(
5714 linear_gradient(
5715 -90.,
5716 linear_color_stop(background, 0.),
5717 linear_color_stop(background.opacity(0.), 1.),
5718 ),
5719 ),
5720 )
5721 })
5722 .child(
5723 div()
5724 .w(px(2.))
5725 .h_full()
5726 .bg(cursor_color)
5727 .absolute()
5728 .top_0()
5729 .left(cursor_relative_position),
5730 ),
5731 )
5732 }),
5733 )
5734 }
5735 }
5736 }
5737
5738 fn render_context_menu(
5739 &self,
5740 style: &EditorStyle,
5741 max_height_in_lines: u32,
5742 y_flipped: bool,
5743 window: &mut Window,
5744 cx: &mut Context<Editor>,
5745 ) -> Option<AnyElement> {
5746 let menu = self.context_menu.borrow();
5747 let menu = menu.as_ref()?;
5748 if !menu.visible() {
5749 return None;
5750 };
5751 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
5752 }
5753
5754 fn render_context_menu_aside(
5755 &self,
5756 style: &EditorStyle,
5757 max_size: Size<Pixels>,
5758 cx: &mut Context<Editor>,
5759 ) -> Option<AnyElement> {
5760 self.context_menu.borrow().as_ref().and_then(|menu| {
5761 if menu.visible() {
5762 menu.render_aside(
5763 style,
5764 max_size,
5765 self.workspace.as_ref().map(|(w, _)| w.clone()),
5766 cx,
5767 )
5768 } else {
5769 None
5770 }
5771 })
5772 }
5773
5774 fn hide_context_menu(
5775 &mut self,
5776 window: &mut Window,
5777 cx: &mut Context<Self>,
5778 ) -> Option<CodeContextMenu> {
5779 cx.notify();
5780 self.completion_tasks.clear();
5781 let context_menu = self.context_menu.borrow_mut().take();
5782 self.stale_inline_completion_in_menu.take();
5783 if context_menu.is_some() {
5784 self.update_visible_inline_completion(window, cx);
5785 }
5786 context_menu
5787 }
5788
5789 fn show_snippet_choices(
5790 &mut self,
5791 choices: &Vec<String>,
5792 selection: Range<Anchor>,
5793 cx: &mut Context<Self>,
5794 ) {
5795 if selection.start.buffer_id.is_none() {
5796 return;
5797 }
5798 let buffer_id = selection.start.buffer_id.unwrap();
5799 let buffer = self.buffer().read(cx).buffer(buffer_id);
5800 let id = post_inc(&mut self.next_completion_id);
5801
5802 if let Some(buffer) = buffer {
5803 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
5804 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
5805 ));
5806 }
5807 }
5808
5809 pub fn insert_snippet(
5810 &mut self,
5811 insertion_ranges: &[Range<usize>],
5812 snippet: Snippet,
5813 window: &mut Window,
5814 cx: &mut Context<Self>,
5815 ) -> Result<()> {
5816 struct Tabstop<T> {
5817 is_end_tabstop: bool,
5818 ranges: Vec<Range<T>>,
5819 choices: Option<Vec<String>>,
5820 }
5821
5822 let tabstops = self.buffer.update(cx, |buffer, cx| {
5823 let snippet_text: Arc<str> = snippet.text.clone().into();
5824 buffer.edit(
5825 insertion_ranges
5826 .iter()
5827 .cloned()
5828 .map(|range| (range, snippet_text.clone())),
5829 Some(AutoindentMode::EachLine),
5830 cx,
5831 );
5832
5833 let snapshot = &*buffer.read(cx);
5834 let snippet = &snippet;
5835 snippet
5836 .tabstops
5837 .iter()
5838 .map(|tabstop| {
5839 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5840 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5841 });
5842 let mut tabstop_ranges = tabstop
5843 .ranges
5844 .iter()
5845 .flat_map(|tabstop_range| {
5846 let mut delta = 0_isize;
5847 insertion_ranges.iter().map(move |insertion_range| {
5848 let insertion_start = insertion_range.start as isize + delta;
5849 delta +=
5850 snippet.text.len() as isize - insertion_range.len() as isize;
5851
5852 let start = ((insertion_start + tabstop_range.start) as usize)
5853 .min(snapshot.len());
5854 let end = ((insertion_start + tabstop_range.end) as usize)
5855 .min(snapshot.len());
5856 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5857 })
5858 })
5859 .collect::<Vec<_>>();
5860 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5861
5862 Tabstop {
5863 is_end_tabstop,
5864 ranges: tabstop_ranges,
5865 choices: tabstop.choices.clone(),
5866 }
5867 })
5868 .collect::<Vec<_>>()
5869 });
5870 if let Some(tabstop) = tabstops.first() {
5871 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5872 s.select_ranges(tabstop.ranges.iter().cloned());
5873 });
5874
5875 if let Some(choices) = &tabstop.choices {
5876 if let Some(selection) = tabstop.ranges.first() {
5877 self.show_snippet_choices(choices, selection.clone(), cx)
5878 }
5879 }
5880
5881 // If we're already at the last tabstop and it's at the end of the snippet,
5882 // we're done, we don't need to keep the state around.
5883 if !tabstop.is_end_tabstop {
5884 let choices = tabstops
5885 .iter()
5886 .map(|tabstop| tabstop.choices.clone())
5887 .collect();
5888
5889 let ranges = tabstops
5890 .into_iter()
5891 .map(|tabstop| tabstop.ranges)
5892 .collect::<Vec<_>>();
5893
5894 self.snippet_stack.push(SnippetState {
5895 active_index: 0,
5896 ranges,
5897 choices,
5898 });
5899 }
5900
5901 // Check whether the just-entered snippet ends with an auto-closable bracket.
5902 if self.autoclose_regions.is_empty() {
5903 let snapshot = self.buffer.read(cx).snapshot(cx);
5904 for selection in &mut self.selections.all::<Point>(cx) {
5905 let selection_head = selection.head();
5906 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5907 continue;
5908 };
5909
5910 let mut bracket_pair = None;
5911 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5912 let prev_chars = snapshot
5913 .reversed_chars_at(selection_head)
5914 .collect::<String>();
5915 for (pair, enabled) in scope.brackets() {
5916 if enabled
5917 && pair.close
5918 && prev_chars.starts_with(pair.start.as_str())
5919 && next_chars.starts_with(pair.end.as_str())
5920 {
5921 bracket_pair = Some(pair.clone());
5922 break;
5923 }
5924 }
5925 if let Some(pair) = bracket_pair {
5926 let start = snapshot.anchor_after(selection_head);
5927 let end = snapshot.anchor_after(selection_head);
5928 self.autoclose_regions.push(AutocloseRegion {
5929 selection_id: selection.id,
5930 range: start..end,
5931 pair,
5932 });
5933 }
5934 }
5935 }
5936 }
5937 Ok(())
5938 }
5939
5940 pub fn move_to_next_snippet_tabstop(
5941 &mut self,
5942 window: &mut Window,
5943 cx: &mut Context<Self>,
5944 ) -> bool {
5945 self.move_to_snippet_tabstop(Bias::Right, window, cx)
5946 }
5947
5948 pub fn move_to_prev_snippet_tabstop(
5949 &mut self,
5950 window: &mut Window,
5951 cx: &mut Context<Self>,
5952 ) -> bool {
5953 self.move_to_snippet_tabstop(Bias::Left, window, cx)
5954 }
5955
5956 pub fn move_to_snippet_tabstop(
5957 &mut self,
5958 bias: Bias,
5959 window: &mut Window,
5960 cx: &mut Context<Self>,
5961 ) -> bool {
5962 if let Some(mut snippet) = self.snippet_stack.pop() {
5963 match bias {
5964 Bias::Left => {
5965 if snippet.active_index > 0 {
5966 snippet.active_index -= 1;
5967 } else {
5968 self.snippet_stack.push(snippet);
5969 return false;
5970 }
5971 }
5972 Bias::Right => {
5973 if snippet.active_index + 1 < snippet.ranges.len() {
5974 snippet.active_index += 1;
5975 } else {
5976 self.snippet_stack.push(snippet);
5977 return false;
5978 }
5979 }
5980 }
5981 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5982 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
5983 s.select_anchor_ranges(current_ranges.iter().cloned())
5984 });
5985
5986 if let Some(choices) = &snippet.choices[snippet.active_index] {
5987 if let Some(selection) = current_ranges.first() {
5988 self.show_snippet_choices(&choices, selection.clone(), cx);
5989 }
5990 }
5991
5992 // If snippet state is not at the last tabstop, push it back on the stack
5993 if snippet.active_index + 1 < snippet.ranges.len() {
5994 self.snippet_stack.push(snippet);
5995 }
5996 return true;
5997 }
5998 }
5999
6000 false
6001 }
6002
6003 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6004 self.transact(window, cx, |this, window, cx| {
6005 this.select_all(&SelectAll, window, cx);
6006 this.insert("", window, cx);
6007 });
6008 }
6009
6010 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
6011 self.transact(window, cx, |this, window, cx| {
6012 this.select_autoclose_pair(window, cx);
6013 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
6014 if !this.linked_edit_ranges.is_empty() {
6015 let selections = this.selections.all::<MultiBufferPoint>(cx);
6016 let snapshot = this.buffer.read(cx).snapshot(cx);
6017
6018 for selection in selections.iter() {
6019 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
6020 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
6021 if selection_start.buffer_id != selection_end.buffer_id {
6022 continue;
6023 }
6024 if let Some(ranges) =
6025 this.linked_editing_ranges_for(selection_start..selection_end, cx)
6026 {
6027 for (buffer, entries) in ranges {
6028 linked_ranges.entry(buffer).or_default().extend(entries);
6029 }
6030 }
6031 }
6032 }
6033
6034 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
6035 if !this.selections.line_mode {
6036 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
6037 for selection in &mut selections {
6038 if selection.is_empty() {
6039 let old_head = selection.head();
6040 let mut new_head =
6041 movement::left(&display_map, old_head.to_display_point(&display_map))
6042 .to_point(&display_map);
6043 if let Some((buffer, line_buffer_range)) = display_map
6044 .buffer_snapshot
6045 .buffer_line_for_row(MultiBufferRow(old_head.row))
6046 {
6047 let indent_size =
6048 buffer.indent_size_for_line(line_buffer_range.start.row);
6049 let indent_len = match indent_size.kind {
6050 IndentKind::Space => {
6051 buffer.settings_at(line_buffer_range.start, cx).tab_size
6052 }
6053 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
6054 };
6055 if old_head.column <= indent_size.len && old_head.column > 0 {
6056 let indent_len = indent_len.get();
6057 new_head = cmp::min(
6058 new_head,
6059 MultiBufferPoint::new(
6060 old_head.row,
6061 ((old_head.column - 1) / indent_len) * indent_len,
6062 ),
6063 );
6064 }
6065 }
6066
6067 selection.set_head(new_head, SelectionGoal::None);
6068 }
6069 }
6070 }
6071
6072 this.signature_help_state.set_backspace_pressed(true);
6073 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6074 s.select(selections)
6075 });
6076 this.insert("", window, cx);
6077 let empty_str: Arc<str> = Arc::from("");
6078 for (buffer, edits) in linked_ranges {
6079 let snapshot = buffer.read(cx).snapshot();
6080 use text::ToPoint as TP;
6081
6082 let edits = edits
6083 .into_iter()
6084 .map(|range| {
6085 let end_point = TP::to_point(&range.end, &snapshot);
6086 let mut start_point = TP::to_point(&range.start, &snapshot);
6087
6088 if end_point == start_point {
6089 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
6090 .saturating_sub(1);
6091 start_point =
6092 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
6093 };
6094
6095 (start_point..end_point, empty_str.clone())
6096 })
6097 .sorted_by_key(|(range, _)| range.start)
6098 .collect::<Vec<_>>();
6099 buffer.update(cx, |this, cx| {
6100 this.edit(edits, None, cx);
6101 })
6102 }
6103 this.refresh_inline_completion(true, false, window, cx);
6104 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
6105 });
6106 }
6107
6108 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
6109 self.transact(window, cx, |this, window, cx| {
6110 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6111 let line_mode = s.line_mode;
6112 s.move_with(|map, selection| {
6113 if selection.is_empty() && !line_mode {
6114 let cursor = movement::right(map, selection.head());
6115 selection.end = cursor;
6116 selection.reversed = true;
6117 selection.goal = SelectionGoal::None;
6118 }
6119 })
6120 });
6121 this.insert("", window, cx);
6122 this.refresh_inline_completion(true, false, window, cx);
6123 });
6124 }
6125
6126 pub fn tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
6127 if self.move_to_prev_snippet_tabstop(window, cx) {
6128 return;
6129 }
6130
6131 self.outdent(&Outdent, window, cx);
6132 }
6133
6134 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
6135 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
6136 return;
6137 }
6138
6139 let mut selections = self.selections.all_adjusted(cx);
6140 let buffer = self.buffer.read(cx);
6141 let snapshot = buffer.snapshot(cx);
6142 let rows_iter = selections.iter().map(|s| s.head().row);
6143 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
6144
6145 let mut edits = Vec::new();
6146 let mut prev_edited_row = 0;
6147 let mut row_delta = 0;
6148 for selection in &mut selections {
6149 if selection.start.row != prev_edited_row {
6150 row_delta = 0;
6151 }
6152 prev_edited_row = selection.end.row;
6153
6154 // If the selection is non-empty, then increase the indentation of the selected lines.
6155 if !selection.is_empty() {
6156 row_delta =
6157 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6158 continue;
6159 }
6160
6161 // If the selection is empty and the cursor is in the leading whitespace before the
6162 // suggested indentation, then auto-indent the line.
6163 let cursor = selection.head();
6164 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
6165 if let Some(suggested_indent) =
6166 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
6167 {
6168 if cursor.column < suggested_indent.len
6169 && cursor.column <= current_indent.len
6170 && current_indent.len <= suggested_indent.len
6171 {
6172 selection.start = Point::new(cursor.row, suggested_indent.len);
6173 selection.end = selection.start;
6174 if row_delta == 0 {
6175 edits.extend(Buffer::edit_for_indent_size_adjustment(
6176 cursor.row,
6177 current_indent,
6178 suggested_indent,
6179 ));
6180 row_delta = suggested_indent.len - current_indent.len;
6181 }
6182 continue;
6183 }
6184 }
6185
6186 // Otherwise, insert a hard or soft tab.
6187 let settings = buffer.settings_at(cursor, cx);
6188 let tab_size = if settings.hard_tabs {
6189 IndentSize::tab()
6190 } else {
6191 let tab_size = settings.tab_size.get();
6192 let char_column = snapshot
6193 .text_for_range(Point::new(cursor.row, 0)..cursor)
6194 .flat_map(str::chars)
6195 .count()
6196 + row_delta as usize;
6197 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
6198 IndentSize::spaces(chars_to_next_tab_stop)
6199 };
6200 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
6201 selection.end = selection.start;
6202 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
6203 row_delta += tab_size.len;
6204 }
6205
6206 self.transact(window, cx, |this, window, cx| {
6207 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6208 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6209 s.select(selections)
6210 });
6211 this.refresh_inline_completion(true, false, window, cx);
6212 });
6213 }
6214
6215 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
6216 if self.read_only(cx) {
6217 return;
6218 }
6219 let mut selections = self.selections.all::<Point>(cx);
6220 let mut prev_edited_row = 0;
6221 let mut row_delta = 0;
6222 let mut edits = Vec::new();
6223 let buffer = self.buffer.read(cx);
6224 let snapshot = buffer.snapshot(cx);
6225 for selection in &mut selections {
6226 if selection.start.row != prev_edited_row {
6227 row_delta = 0;
6228 }
6229 prev_edited_row = selection.end.row;
6230
6231 row_delta =
6232 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
6233 }
6234
6235 self.transact(window, cx, |this, window, cx| {
6236 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
6237 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6238 s.select(selections)
6239 });
6240 });
6241 }
6242
6243 fn indent_selection(
6244 buffer: &MultiBuffer,
6245 snapshot: &MultiBufferSnapshot,
6246 selection: &mut Selection<Point>,
6247 edits: &mut Vec<(Range<Point>, String)>,
6248 delta_for_start_row: u32,
6249 cx: &App,
6250 ) -> u32 {
6251 let settings = buffer.settings_at(selection.start, cx);
6252 let tab_size = settings.tab_size.get();
6253 let indent_kind = if settings.hard_tabs {
6254 IndentKind::Tab
6255 } else {
6256 IndentKind::Space
6257 };
6258 let mut start_row = selection.start.row;
6259 let mut end_row = selection.end.row + 1;
6260
6261 // If a selection ends at the beginning of a line, don't indent
6262 // that last line.
6263 if selection.end.column == 0 && selection.end.row > selection.start.row {
6264 end_row -= 1;
6265 }
6266
6267 // Avoid re-indenting a row that has already been indented by a
6268 // previous selection, but still update this selection's column
6269 // to reflect that indentation.
6270 if delta_for_start_row > 0 {
6271 start_row += 1;
6272 selection.start.column += delta_for_start_row;
6273 if selection.end.row == selection.start.row {
6274 selection.end.column += delta_for_start_row;
6275 }
6276 }
6277
6278 let mut delta_for_end_row = 0;
6279 let has_multiple_rows = start_row + 1 != end_row;
6280 for row in start_row..end_row {
6281 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
6282 let indent_delta = match (current_indent.kind, indent_kind) {
6283 (IndentKind::Space, IndentKind::Space) => {
6284 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
6285 IndentSize::spaces(columns_to_next_tab_stop)
6286 }
6287 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
6288 (_, IndentKind::Tab) => IndentSize::tab(),
6289 };
6290
6291 let start = if has_multiple_rows || current_indent.len < selection.start.column {
6292 0
6293 } else {
6294 selection.start.column
6295 };
6296 let row_start = Point::new(row, start);
6297 edits.push((
6298 row_start..row_start,
6299 indent_delta.chars().collect::<String>(),
6300 ));
6301
6302 // Update this selection's endpoints to reflect the indentation.
6303 if row == selection.start.row {
6304 selection.start.column += indent_delta.len;
6305 }
6306 if row == selection.end.row {
6307 selection.end.column += indent_delta.len;
6308 delta_for_end_row = indent_delta.len;
6309 }
6310 }
6311
6312 if selection.start.row == selection.end.row {
6313 delta_for_start_row + delta_for_end_row
6314 } else {
6315 delta_for_end_row
6316 }
6317 }
6318
6319 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
6320 if self.read_only(cx) {
6321 return;
6322 }
6323 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6324 let selections = self.selections.all::<Point>(cx);
6325 let mut deletion_ranges = Vec::new();
6326 let mut last_outdent = None;
6327 {
6328 let buffer = self.buffer.read(cx);
6329 let snapshot = buffer.snapshot(cx);
6330 for selection in &selections {
6331 let settings = buffer.settings_at(selection.start, cx);
6332 let tab_size = settings.tab_size.get();
6333 let mut rows = selection.spanned_rows(false, &display_map);
6334
6335 // Avoid re-outdenting a row that has already been outdented by a
6336 // previous selection.
6337 if let Some(last_row) = last_outdent {
6338 if last_row == rows.start {
6339 rows.start = rows.start.next_row();
6340 }
6341 }
6342 let has_multiple_rows = rows.len() > 1;
6343 for row in rows.iter_rows() {
6344 let indent_size = snapshot.indent_size_for_line(row);
6345 if indent_size.len > 0 {
6346 let deletion_len = match indent_size.kind {
6347 IndentKind::Space => {
6348 let columns_to_prev_tab_stop = indent_size.len % tab_size;
6349 if columns_to_prev_tab_stop == 0 {
6350 tab_size
6351 } else {
6352 columns_to_prev_tab_stop
6353 }
6354 }
6355 IndentKind::Tab => 1,
6356 };
6357 let start = if has_multiple_rows
6358 || deletion_len > selection.start.column
6359 || indent_size.len < selection.start.column
6360 {
6361 0
6362 } else {
6363 selection.start.column - deletion_len
6364 };
6365 deletion_ranges.push(
6366 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
6367 );
6368 last_outdent = Some(row);
6369 }
6370 }
6371 }
6372 }
6373
6374 self.transact(window, cx, |this, window, cx| {
6375 this.buffer.update(cx, |buffer, cx| {
6376 let empty_str: Arc<str> = Arc::default();
6377 buffer.edit(
6378 deletion_ranges
6379 .into_iter()
6380 .map(|range| (range, empty_str.clone())),
6381 None,
6382 cx,
6383 );
6384 });
6385 let selections = this.selections.all::<usize>(cx);
6386 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6387 s.select(selections)
6388 });
6389 });
6390 }
6391
6392 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
6393 if self.read_only(cx) {
6394 return;
6395 }
6396 let selections = self
6397 .selections
6398 .all::<usize>(cx)
6399 .into_iter()
6400 .map(|s| s.range());
6401
6402 self.transact(window, cx, |this, window, cx| {
6403 this.buffer.update(cx, |buffer, cx| {
6404 buffer.autoindent_ranges(selections, cx);
6405 });
6406 let selections = this.selections.all::<usize>(cx);
6407 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6408 s.select(selections)
6409 });
6410 });
6411 }
6412
6413 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
6414 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6415 let selections = self.selections.all::<Point>(cx);
6416
6417 let mut new_cursors = Vec::new();
6418 let mut edit_ranges = Vec::new();
6419 let mut selections = selections.iter().peekable();
6420 while let Some(selection) = selections.next() {
6421 let mut rows = selection.spanned_rows(false, &display_map);
6422 let goal_display_column = selection.head().to_display_point(&display_map).column();
6423
6424 // Accumulate contiguous regions of rows that we want to delete.
6425 while let Some(next_selection) = selections.peek() {
6426 let next_rows = next_selection.spanned_rows(false, &display_map);
6427 if next_rows.start <= rows.end {
6428 rows.end = next_rows.end;
6429 selections.next().unwrap();
6430 } else {
6431 break;
6432 }
6433 }
6434
6435 let buffer = &display_map.buffer_snapshot;
6436 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
6437 let edit_end;
6438 let cursor_buffer_row;
6439 if buffer.max_point().row >= rows.end.0 {
6440 // If there's a line after the range, delete the \n from the end of the row range
6441 // and position the cursor on the next line.
6442 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
6443 cursor_buffer_row = rows.end;
6444 } else {
6445 // If there isn't a line after the range, delete the \n from the line before the
6446 // start of the row range and position the cursor there.
6447 edit_start = edit_start.saturating_sub(1);
6448 edit_end = buffer.len();
6449 cursor_buffer_row = rows.start.previous_row();
6450 }
6451
6452 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
6453 *cursor.column_mut() =
6454 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
6455
6456 new_cursors.push((
6457 selection.id,
6458 buffer.anchor_after(cursor.to_point(&display_map)),
6459 ));
6460 edit_ranges.push(edit_start..edit_end);
6461 }
6462
6463 self.transact(window, cx, |this, window, cx| {
6464 let buffer = this.buffer.update(cx, |buffer, cx| {
6465 let empty_str: Arc<str> = Arc::default();
6466 buffer.edit(
6467 edit_ranges
6468 .into_iter()
6469 .map(|range| (range, empty_str.clone())),
6470 None,
6471 cx,
6472 );
6473 buffer.snapshot(cx)
6474 });
6475 let new_selections = new_cursors
6476 .into_iter()
6477 .map(|(id, cursor)| {
6478 let cursor = cursor.to_point(&buffer);
6479 Selection {
6480 id,
6481 start: cursor,
6482 end: cursor,
6483 reversed: false,
6484 goal: SelectionGoal::None,
6485 }
6486 })
6487 .collect();
6488
6489 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6490 s.select(new_selections);
6491 });
6492 });
6493 }
6494
6495 pub fn join_lines_impl(
6496 &mut self,
6497 insert_whitespace: bool,
6498 window: &mut Window,
6499 cx: &mut Context<Self>,
6500 ) {
6501 if self.read_only(cx) {
6502 return;
6503 }
6504 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
6505 for selection in self.selections.all::<Point>(cx) {
6506 let start = MultiBufferRow(selection.start.row);
6507 // Treat single line selections as if they include the next line. Otherwise this action
6508 // would do nothing for single line selections individual cursors.
6509 let end = if selection.start.row == selection.end.row {
6510 MultiBufferRow(selection.start.row + 1)
6511 } else {
6512 MultiBufferRow(selection.end.row)
6513 };
6514
6515 if let Some(last_row_range) = row_ranges.last_mut() {
6516 if start <= last_row_range.end {
6517 last_row_range.end = end;
6518 continue;
6519 }
6520 }
6521 row_ranges.push(start..end);
6522 }
6523
6524 let snapshot = self.buffer.read(cx).snapshot(cx);
6525 let mut cursor_positions = Vec::new();
6526 for row_range in &row_ranges {
6527 let anchor = snapshot.anchor_before(Point::new(
6528 row_range.end.previous_row().0,
6529 snapshot.line_len(row_range.end.previous_row()),
6530 ));
6531 cursor_positions.push(anchor..anchor);
6532 }
6533
6534 self.transact(window, cx, |this, window, cx| {
6535 for row_range in row_ranges.into_iter().rev() {
6536 for row in row_range.iter_rows().rev() {
6537 let end_of_line = Point::new(row.0, snapshot.line_len(row));
6538 let next_line_row = row.next_row();
6539 let indent = snapshot.indent_size_for_line(next_line_row);
6540 let start_of_next_line = Point::new(next_line_row.0, indent.len);
6541
6542 let replace =
6543 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
6544 " "
6545 } else {
6546 ""
6547 };
6548
6549 this.buffer.update(cx, |buffer, cx| {
6550 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
6551 });
6552 }
6553 }
6554
6555 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6556 s.select_anchor_ranges(cursor_positions)
6557 });
6558 });
6559 }
6560
6561 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
6562 self.join_lines_impl(true, window, cx);
6563 }
6564
6565 pub fn sort_lines_case_sensitive(
6566 &mut self,
6567 _: &SortLinesCaseSensitive,
6568 window: &mut Window,
6569 cx: &mut Context<Self>,
6570 ) {
6571 self.manipulate_lines(window, cx, |lines| lines.sort())
6572 }
6573
6574 pub fn sort_lines_case_insensitive(
6575 &mut self,
6576 _: &SortLinesCaseInsensitive,
6577 window: &mut Window,
6578 cx: &mut Context<Self>,
6579 ) {
6580 self.manipulate_lines(window, cx, |lines| {
6581 lines.sort_by_key(|line| line.to_lowercase())
6582 })
6583 }
6584
6585 pub fn unique_lines_case_insensitive(
6586 &mut self,
6587 _: &UniqueLinesCaseInsensitive,
6588 window: &mut Window,
6589 cx: &mut Context<Self>,
6590 ) {
6591 self.manipulate_lines(window, cx, |lines| {
6592 let mut seen = HashSet::default();
6593 lines.retain(|line| seen.insert(line.to_lowercase()));
6594 })
6595 }
6596
6597 pub fn unique_lines_case_sensitive(
6598 &mut self,
6599 _: &UniqueLinesCaseSensitive,
6600 window: &mut Window,
6601 cx: &mut Context<Self>,
6602 ) {
6603 self.manipulate_lines(window, cx, |lines| {
6604 let mut seen = HashSet::default();
6605 lines.retain(|line| seen.insert(*line));
6606 })
6607 }
6608
6609 pub fn revert_file(&mut self, _: &RevertFile, window: &mut Window, cx: &mut Context<Self>) {
6610 let mut revert_changes = HashMap::default();
6611 let snapshot = self.snapshot(window, cx);
6612 for hunk in snapshot
6613 .hunks_for_ranges(Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter())
6614 {
6615 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6616 }
6617 if !revert_changes.is_empty() {
6618 self.transact(window, cx, |editor, window, cx| {
6619 editor.revert(revert_changes, window, cx);
6620 });
6621 }
6622 }
6623
6624 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
6625 let Some(project) = self.project.clone() else {
6626 return;
6627 };
6628 self.reload(project, window, cx)
6629 .detach_and_notify_err(window, cx);
6630 }
6631
6632 pub fn revert_selected_hunks(
6633 &mut self,
6634 _: &RevertSelectedHunks,
6635 window: &mut Window,
6636 cx: &mut Context<Self>,
6637 ) {
6638 let selections = self.selections.all(cx).into_iter().map(|s| s.range());
6639 self.revert_hunks_in_ranges(selections, window, cx);
6640 }
6641
6642 fn revert_hunks_in_ranges(
6643 &mut self,
6644 ranges: impl Iterator<Item = Range<Point>>,
6645 window: &mut Window,
6646 cx: &mut Context<Editor>,
6647 ) {
6648 let mut revert_changes = HashMap::default();
6649 let snapshot = self.snapshot(window, cx);
6650 for hunk in &snapshot.hunks_for_ranges(ranges) {
6651 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6652 }
6653 if !revert_changes.is_empty() {
6654 self.transact(window, cx, |editor, window, cx| {
6655 editor.revert(revert_changes, window, cx);
6656 });
6657 }
6658 }
6659
6660 pub fn open_active_item_in_terminal(
6661 &mut self,
6662 _: &OpenInTerminal,
6663 window: &mut Window,
6664 cx: &mut Context<Self>,
6665 ) {
6666 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6667 let project_path = buffer.read(cx).project_path(cx)?;
6668 let project = self.project.as_ref()?.read(cx);
6669 let entry = project.entry_for_path(&project_path, cx)?;
6670 let parent = match &entry.canonical_path {
6671 Some(canonical_path) => canonical_path.to_path_buf(),
6672 None => project.absolute_path(&project_path, cx)?,
6673 }
6674 .parent()?
6675 .to_path_buf();
6676 Some(parent)
6677 }) {
6678 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
6679 }
6680 }
6681
6682 pub fn prepare_revert_change(
6683 &self,
6684 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6685 hunk: &MultiBufferDiffHunk,
6686 cx: &mut App,
6687 ) -> Option<()> {
6688 let buffer = self.buffer.read(cx);
6689 let change_set = buffer.change_set_for(hunk.buffer_id)?;
6690 let buffer = buffer.buffer(hunk.buffer_id)?;
6691 let buffer = buffer.read(cx);
6692 let original_text = change_set
6693 .read(cx)
6694 .base_text
6695 .as_ref()?
6696 .as_rope()
6697 .slice(hunk.diff_base_byte_range.clone());
6698 let buffer_snapshot = buffer.snapshot();
6699 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6700 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6701 probe
6702 .0
6703 .start
6704 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6705 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6706 }) {
6707 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6708 Some(())
6709 } else {
6710 None
6711 }
6712 }
6713
6714 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
6715 self.manipulate_lines(window, cx, |lines| lines.reverse())
6716 }
6717
6718 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
6719 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
6720 }
6721
6722 fn manipulate_lines<Fn>(
6723 &mut self,
6724 window: &mut Window,
6725 cx: &mut Context<Self>,
6726 mut callback: Fn,
6727 ) where
6728 Fn: FnMut(&mut Vec<&str>),
6729 {
6730 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6731 let buffer = self.buffer.read(cx).snapshot(cx);
6732
6733 let mut edits = Vec::new();
6734
6735 let selections = self.selections.all::<Point>(cx);
6736 let mut selections = selections.iter().peekable();
6737 let mut contiguous_row_selections = Vec::new();
6738 let mut new_selections = Vec::new();
6739 let mut added_lines = 0;
6740 let mut removed_lines = 0;
6741
6742 while let Some(selection) = selections.next() {
6743 let (start_row, end_row) = consume_contiguous_rows(
6744 &mut contiguous_row_selections,
6745 selection,
6746 &display_map,
6747 &mut selections,
6748 );
6749
6750 let start_point = Point::new(start_row.0, 0);
6751 let end_point = Point::new(
6752 end_row.previous_row().0,
6753 buffer.line_len(end_row.previous_row()),
6754 );
6755 let text = buffer
6756 .text_for_range(start_point..end_point)
6757 .collect::<String>();
6758
6759 let mut lines = text.split('\n').collect_vec();
6760
6761 let lines_before = lines.len();
6762 callback(&mut lines);
6763 let lines_after = lines.len();
6764
6765 edits.push((start_point..end_point, lines.join("\n")));
6766
6767 // Selections must change based on added and removed line count
6768 let start_row =
6769 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6770 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6771 new_selections.push(Selection {
6772 id: selection.id,
6773 start: start_row,
6774 end: end_row,
6775 goal: SelectionGoal::None,
6776 reversed: selection.reversed,
6777 });
6778
6779 if lines_after > lines_before {
6780 added_lines += lines_after - lines_before;
6781 } else if lines_before > lines_after {
6782 removed_lines += lines_before - lines_after;
6783 }
6784 }
6785
6786 self.transact(window, cx, |this, window, cx| {
6787 let buffer = this.buffer.update(cx, |buffer, cx| {
6788 buffer.edit(edits, None, cx);
6789 buffer.snapshot(cx)
6790 });
6791
6792 // Recalculate offsets on newly edited buffer
6793 let new_selections = new_selections
6794 .iter()
6795 .map(|s| {
6796 let start_point = Point::new(s.start.0, 0);
6797 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6798 Selection {
6799 id: s.id,
6800 start: buffer.point_to_offset(start_point),
6801 end: buffer.point_to_offset(end_point),
6802 goal: s.goal,
6803 reversed: s.reversed,
6804 }
6805 })
6806 .collect();
6807
6808 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6809 s.select(new_selections);
6810 });
6811
6812 this.request_autoscroll(Autoscroll::fit(), cx);
6813 });
6814 }
6815
6816 pub fn convert_to_upper_case(
6817 &mut self,
6818 _: &ConvertToUpperCase,
6819 window: &mut Window,
6820 cx: &mut Context<Self>,
6821 ) {
6822 self.manipulate_text(window, cx, |text| text.to_uppercase())
6823 }
6824
6825 pub fn convert_to_lower_case(
6826 &mut self,
6827 _: &ConvertToLowerCase,
6828 window: &mut Window,
6829 cx: &mut Context<Self>,
6830 ) {
6831 self.manipulate_text(window, cx, |text| text.to_lowercase())
6832 }
6833
6834 pub fn convert_to_title_case(
6835 &mut self,
6836 _: &ConvertToTitleCase,
6837 window: &mut Window,
6838 cx: &mut Context<Self>,
6839 ) {
6840 self.manipulate_text(window, cx, |text| {
6841 text.split('\n')
6842 .map(|line| line.to_case(Case::Title))
6843 .join("\n")
6844 })
6845 }
6846
6847 pub fn convert_to_snake_case(
6848 &mut self,
6849 _: &ConvertToSnakeCase,
6850 window: &mut Window,
6851 cx: &mut Context<Self>,
6852 ) {
6853 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
6854 }
6855
6856 pub fn convert_to_kebab_case(
6857 &mut self,
6858 _: &ConvertToKebabCase,
6859 window: &mut Window,
6860 cx: &mut Context<Self>,
6861 ) {
6862 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
6863 }
6864
6865 pub fn convert_to_upper_camel_case(
6866 &mut self,
6867 _: &ConvertToUpperCamelCase,
6868 window: &mut Window,
6869 cx: &mut Context<Self>,
6870 ) {
6871 self.manipulate_text(window, cx, |text| {
6872 text.split('\n')
6873 .map(|line| line.to_case(Case::UpperCamel))
6874 .join("\n")
6875 })
6876 }
6877
6878 pub fn convert_to_lower_camel_case(
6879 &mut self,
6880 _: &ConvertToLowerCamelCase,
6881 window: &mut Window,
6882 cx: &mut Context<Self>,
6883 ) {
6884 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
6885 }
6886
6887 pub fn convert_to_opposite_case(
6888 &mut self,
6889 _: &ConvertToOppositeCase,
6890 window: &mut Window,
6891 cx: &mut Context<Self>,
6892 ) {
6893 self.manipulate_text(window, cx, |text| {
6894 text.chars()
6895 .fold(String::with_capacity(text.len()), |mut t, c| {
6896 if c.is_uppercase() {
6897 t.extend(c.to_lowercase());
6898 } else {
6899 t.extend(c.to_uppercase());
6900 }
6901 t
6902 })
6903 })
6904 }
6905
6906 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
6907 where
6908 Fn: FnMut(&str) -> String,
6909 {
6910 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6911 let buffer = self.buffer.read(cx).snapshot(cx);
6912
6913 let mut new_selections = Vec::new();
6914 let mut edits = Vec::new();
6915 let mut selection_adjustment = 0i32;
6916
6917 for selection in self.selections.all::<usize>(cx) {
6918 let selection_is_empty = selection.is_empty();
6919
6920 let (start, end) = if selection_is_empty {
6921 let word_range = movement::surrounding_word(
6922 &display_map,
6923 selection.start.to_display_point(&display_map),
6924 );
6925 let start = word_range.start.to_offset(&display_map, Bias::Left);
6926 let end = word_range.end.to_offset(&display_map, Bias::Left);
6927 (start, end)
6928 } else {
6929 (selection.start, selection.end)
6930 };
6931
6932 let text = buffer.text_for_range(start..end).collect::<String>();
6933 let old_length = text.len() as i32;
6934 let text = callback(&text);
6935
6936 new_selections.push(Selection {
6937 start: (start as i32 - selection_adjustment) as usize,
6938 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6939 goal: SelectionGoal::None,
6940 ..selection
6941 });
6942
6943 selection_adjustment += old_length - text.len() as i32;
6944
6945 edits.push((start..end, text));
6946 }
6947
6948 self.transact(window, cx, |this, window, cx| {
6949 this.buffer.update(cx, |buffer, cx| {
6950 buffer.edit(edits, None, cx);
6951 });
6952
6953 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
6954 s.select(new_selections);
6955 });
6956
6957 this.request_autoscroll(Autoscroll::fit(), cx);
6958 });
6959 }
6960
6961 pub fn duplicate(
6962 &mut self,
6963 upwards: bool,
6964 whole_lines: bool,
6965 window: &mut Window,
6966 cx: &mut Context<Self>,
6967 ) {
6968 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6969 let buffer = &display_map.buffer_snapshot;
6970 let selections = self.selections.all::<Point>(cx);
6971
6972 let mut edits = Vec::new();
6973 let mut selections_iter = selections.iter().peekable();
6974 while let Some(selection) = selections_iter.next() {
6975 let mut rows = selection.spanned_rows(false, &display_map);
6976 // duplicate line-wise
6977 if whole_lines || selection.start == selection.end {
6978 // Avoid duplicating the same lines twice.
6979 while let Some(next_selection) = selections_iter.peek() {
6980 let next_rows = next_selection.spanned_rows(false, &display_map);
6981 if next_rows.start < rows.end {
6982 rows.end = next_rows.end;
6983 selections_iter.next().unwrap();
6984 } else {
6985 break;
6986 }
6987 }
6988
6989 // Copy the text from the selected row region and splice it either at the start
6990 // or end of the region.
6991 let start = Point::new(rows.start.0, 0);
6992 let end = Point::new(
6993 rows.end.previous_row().0,
6994 buffer.line_len(rows.end.previous_row()),
6995 );
6996 let text = buffer
6997 .text_for_range(start..end)
6998 .chain(Some("\n"))
6999 .collect::<String>();
7000 let insert_location = if upwards {
7001 Point::new(rows.end.0, 0)
7002 } else {
7003 start
7004 };
7005 edits.push((insert_location..insert_location, text));
7006 } else {
7007 // duplicate character-wise
7008 let start = selection.start;
7009 let end = selection.end;
7010 let text = buffer.text_for_range(start..end).collect::<String>();
7011 edits.push((selection.end..selection.end, text));
7012 }
7013 }
7014
7015 self.transact(window, cx, |this, _, cx| {
7016 this.buffer.update(cx, |buffer, cx| {
7017 buffer.edit(edits, None, cx);
7018 });
7019
7020 this.request_autoscroll(Autoscroll::fit(), cx);
7021 });
7022 }
7023
7024 pub fn duplicate_line_up(
7025 &mut self,
7026 _: &DuplicateLineUp,
7027 window: &mut Window,
7028 cx: &mut Context<Self>,
7029 ) {
7030 self.duplicate(true, true, window, cx);
7031 }
7032
7033 pub fn duplicate_line_down(
7034 &mut self,
7035 _: &DuplicateLineDown,
7036 window: &mut Window,
7037 cx: &mut Context<Self>,
7038 ) {
7039 self.duplicate(false, true, window, cx);
7040 }
7041
7042 pub fn duplicate_selection(
7043 &mut self,
7044 _: &DuplicateSelection,
7045 window: &mut Window,
7046 cx: &mut Context<Self>,
7047 ) {
7048 self.duplicate(false, false, window, cx);
7049 }
7050
7051 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
7052 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7053 let buffer = self.buffer.read(cx).snapshot(cx);
7054
7055 let mut edits = Vec::new();
7056 let mut unfold_ranges = Vec::new();
7057 let mut refold_creases = Vec::new();
7058
7059 let selections = self.selections.all::<Point>(cx);
7060 let mut selections = selections.iter().peekable();
7061 let mut contiguous_row_selections = Vec::new();
7062 let mut new_selections = Vec::new();
7063
7064 while let Some(selection) = selections.next() {
7065 // Find all the selections that span a contiguous row range
7066 let (start_row, end_row) = consume_contiguous_rows(
7067 &mut contiguous_row_selections,
7068 selection,
7069 &display_map,
7070 &mut selections,
7071 );
7072
7073 // Move the text spanned by the row range to be before the line preceding the row range
7074 if start_row.0 > 0 {
7075 let range_to_move = Point::new(
7076 start_row.previous_row().0,
7077 buffer.line_len(start_row.previous_row()),
7078 )
7079 ..Point::new(
7080 end_row.previous_row().0,
7081 buffer.line_len(end_row.previous_row()),
7082 );
7083 let insertion_point = display_map
7084 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
7085 .0;
7086
7087 // Don't move lines across excerpts
7088 if buffer
7089 .excerpt_containing(insertion_point..range_to_move.end)
7090 .is_some()
7091 {
7092 let text = buffer
7093 .text_for_range(range_to_move.clone())
7094 .flat_map(|s| s.chars())
7095 .skip(1)
7096 .chain(['\n'])
7097 .collect::<String>();
7098
7099 edits.push((
7100 buffer.anchor_after(range_to_move.start)
7101 ..buffer.anchor_before(range_to_move.end),
7102 String::new(),
7103 ));
7104 let insertion_anchor = buffer.anchor_after(insertion_point);
7105 edits.push((insertion_anchor..insertion_anchor, text));
7106
7107 let row_delta = range_to_move.start.row - insertion_point.row + 1;
7108
7109 // Move selections up
7110 new_selections.extend(contiguous_row_selections.drain(..).map(
7111 |mut selection| {
7112 selection.start.row -= row_delta;
7113 selection.end.row -= row_delta;
7114 selection
7115 },
7116 ));
7117
7118 // Move folds up
7119 unfold_ranges.push(range_to_move.clone());
7120 for fold in display_map.folds_in_range(
7121 buffer.anchor_before(range_to_move.start)
7122 ..buffer.anchor_after(range_to_move.end),
7123 ) {
7124 let mut start = fold.range.start.to_point(&buffer);
7125 let mut end = fold.range.end.to_point(&buffer);
7126 start.row -= row_delta;
7127 end.row -= row_delta;
7128 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7129 }
7130 }
7131 }
7132
7133 // If we didn't move line(s), preserve the existing selections
7134 new_selections.append(&mut contiguous_row_selections);
7135 }
7136
7137 self.transact(window, cx, |this, window, cx| {
7138 this.unfold_ranges(&unfold_ranges, true, true, cx);
7139 this.buffer.update(cx, |buffer, cx| {
7140 for (range, text) in edits {
7141 buffer.edit([(range, text)], None, cx);
7142 }
7143 });
7144 this.fold_creases(refold_creases, true, window, cx);
7145 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7146 s.select(new_selections);
7147 })
7148 });
7149 }
7150
7151 pub fn move_line_down(
7152 &mut self,
7153 _: &MoveLineDown,
7154 window: &mut Window,
7155 cx: &mut Context<Self>,
7156 ) {
7157 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7158 let buffer = self.buffer.read(cx).snapshot(cx);
7159
7160 let mut edits = Vec::new();
7161 let mut unfold_ranges = Vec::new();
7162 let mut refold_creases = Vec::new();
7163
7164 let selections = self.selections.all::<Point>(cx);
7165 let mut selections = selections.iter().peekable();
7166 let mut contiguous_row_selections = Vec::new();
7167 let mut new_selections = Vec::new();
7168
7169 while let Some(selection) = selections.next() {
7170 // Find all the selections that span a contiguous row range
7171 let (start_row, end_row) = consume_contiguous_rows(
7172 &mut contiguous_row_selections,
7173 selection,
7174 &display_map,
7175 &mut selections,
7176 );
7177
7178 // Move the text spanned by the row range to be after the last line of the row range
7179 if end_row.0 <= buffer.max_point().row {
7180 let range_to_move =
7181 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
7182 let insertion_point = display_map
7183 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
7184 .0;
7185
7186 // Don't move lines across excerpt boundaries
7187 if buffer
7188 .excerpt_containing(range_to_move.start..insertion_point)
7189 .is_some()
7190 {
7191 let mut text = String::from("\n");
7192 text.extend(buffer.text_for_range(range_to_move.clone()));
7193 text.pop(); // Drop trailing newline
7194 edits.push((
7195 buffer.anchor_after(range_to_move.start)
7196 ..buffer.anchor_before(range_to_move.end),
7197 String::new(),
7198 ));
7199 let insertion_anchor = buffer.anchor_after(insertion_point);
7200 edits.push((insertion_anchor..insertion_anchor, text));
7201
7202 let row_delta = insertion_point.row - range_to_move.end.row + 1;
7203
7204 // Move selections down
7205 new_selections.extend(contiguous_row_selections.drain(..).map(
7206 |mut selection| {
7207 selection.start.row += row_delta;
7208 selection.end.row += row_delta;
7209 selection
7210 },
7211 ));
7212
7213 // Move folds down
7214 unfold_ranges.push(range_to_move.clone());
7215 for fold in display_map.folds_in_range(
7216 buffer.anchor_before(range_to_move.start)
7217 ..buffer.anchor_after(range_to_move.end),
7218 ) {
7219 let mut start = fold.range.start.to_point(&buffer);
7220 let mut end = fold.range.end.to_point(&buffer);
7221 start.row += row_delta;
7222 end.row += row_delta;
7223 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
7224 }
7225 }
7226 }
7227
7228 // If we didn't move line(s), preserve the existing selections
7229 new_selections.append(&mut contiguous_row_selections);
7230 }
7231
7232 self.transact(window, cx, |this, window, cx| {
7233 this.unfold_ranges(&unfold_ranges, true, true, cx);
7234 this.buffer.update(cx, |buffer, cx| {
7235 for (range, text) in edits {
7236 buffer.edit([(range, text)], None, cx);
7237 }
7238 });
7239 this.fold_creases(refold_creases, true, window, cx);
7240 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7241 s.select(new_selections)
7242 });
7243 });
7244 }
7245
7246 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
7247 let text_layout_details = &self.text_layout_details(window);
7248 self.transact(window, cx, |this, window, cx| {
7249 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7250 let mut edits: Vec<(Range<usize>, String)> = Default::default();
7251 let line_mode = s.line_mode;
7252 s.move_with(|display_map, selection| {
7253 if !selection.is_empty() || line_mode {
7254 return;
7255 }
7256
7257 let mut head = selection.head();
7258 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
7259 if head.column() == display_map.line_len(head.row()) {
7260 transpose_offset = display_map
7261 .buffer_snapshot
7262 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7263 }
7264
7265 if transpose_offset == 0 {
7266 return;
7267 }
7268
7269 *head.column_mut() += 1;
7270 head = display_map.clip_point(head, Bias::Right);
7271 let goal = SelectionGoal::HorizontalPosition(
7272 display_map
7273 .x_for_display_point(head, text_layout_details)
7274 .into(),
7275 );
7276 selection.collapse_to(head, goal);
7277
7278 let transpose_start = display_map
7279 .buffer_snapshot
7280 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
7281 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
7282 let transpose_end = display_map
7283 .buffer_snapshot
7284 .clip_offset(transpose_offset + 1, Bias::Right);
7285 if let Some(ch) =
7286 display_map.buffer_snapshot.chars_at(transpose_start).next()
7287 {
7288 edits.push((transpose_start..transpose_offset, String::new()));
7289 edits.push((transpose_end..transpose_end, ch.to_string()));
7290 }
7291 }
7292 });
7293 edits
7294 });
7295 this.buffer
7296 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7297 let selections = this.selections.all::<usize>(cx);
7298 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7299 s.select(selections);
7300 });
7301 });
7302 }
7303
7304 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
7305 self.rewrap_impl(IsVimMode::No, cx)
7306 }
7307
7308 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut Context<Self>) {
7309 let buffer = self.buffer.read(cx).snapshot(cx);
7310 let selections = self.selections.all::<Point>(cx);
7311 let mut selections = selections.iter().peekable();
7312
7313 let mut edits = Vec::new();
7314 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
7315
7316 while let Some(selection) = selections.next() {
7317 let mut start_row = selection.start.row;
7318 let mut end_row = selection.end.row;
7319
7320 // Skip selections that overlap with a range that has already been rewrapped.
7321 let selection_range = start_row..end_row;
7322 if rewrapped_row_ranges
7323 .iter()
7324 .any(|range| range.overlaps(&selection_range))
7325 {
7326 continue;
7327 }
7328
7329 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
7330
7331 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
7332 match language_scope.language_name().as_ref() {
7333 "Markdown" | "Plain Text" => {
7334 should_rewrap = true;
7335 }
7336 _ => {}
7337 }
7338 }
7339
7340 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
7341
7342 // Since not all lines in the selection may be at the same indent
7343 // level, choose the indent size that is the most common between all
7344 // of the lines.
7345 //
7346 // If there is a tie, we use the deepest indent.
7347 let (indent_size, indent_end) = {
7348 let mut indent_size_occurrences = HashMap::default();
7349 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
7350
7351 for row in start_row..=end_row {
7352 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
7353 rows_by_indent_size.entry(indent).or_default().push(row);
7354 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
7355 }
7356
7357 let indent_size = indent_size_occurrences
7358 .into_iter()
7359 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
7360 .map(|(indent, _)| indent)
7361 .unwrap_or_default();
7362 let row = rows_by_indent_size[&indent_size][0];
7363 let indent_end = Point::new(row, indent_size.len);
7364
7365 (indent_size, indent_end)
7366 };
7367
7368 let mut line_prefix = indent_size.chars().collect::<String>();
7369
7370 if let Some(comment_prefix) =
7371 buffer
7372 .language_scope_at(selection.head())
7373 .and_then(|language| {
7374 language
7375 .line_comment_prefixes()
7376 .iter()
7377 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
7378 .cloned()
7379 })
7380 {
7381 line_prefix.push_str(&comment_prefix);
7382 should_rewrap = true;
7383 }
7384
7385 if !should_rewrap {
7386 continue;
7387 }
7388
7389 if selection.is_empty() {
7390 'expand_upwards: while start_row > 0 {
7391 let prev_row = start_row - 1;
7392 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
7393 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
7394 {
7395 start_row = prev_row;
7396 } else {
7397 break 'expand_upwards;
7398 }
7399 }
7400
7401 'expand_downwards: while end_row < buffer.max_point().row {
7402 let next_row = end_row + 1;
7403 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
7404 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
7405 {
7406 end_row = next_row;
7407 } else {
7408 break 'expand_downwards;
7409 }
7410 }
7411 }
7412
7413 let start = Point::new(start_row, 0);
7414 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
7415 let selection_text = buffer.text_for_range(start..end).collect::<String>();
7416 let Some(lines_without_prefixes) = selection_text
7417 .lines()
7418 .map(|line| {
7419 line.strip_prefix(&line_prefix)
7420 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
7421 .ok_or_else(|| {
7422 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
7423 })
7424 })
7425 .collect::<Result<Vec<_>, _>>()
7426 .log_err()
7427 else {
7428 continue;
7429 };
7430
7431 let wrap_column = buffer
7432 .settings_at(Point::new(start_row, 0), cx)
7433 .preferred_line_length as usize;
7434 let wrapped_text = wrap_with_prefix(
7435 line_prefix,
7436 lines_without_prefixes.join(" "),
7437 wrap_column,
7438 tab_size,
7439 );
7440
7441 // TODO: should always use char-based diff while still supporting cursor behavior that
7442 // matches vim.
7443 let diff = match is_vim_mode {
7444 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
7445 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
7446 };
7447 let mut offset = start.to_offset(&buffer);
7448 let mut moved_since_edit = true;
7449
7450 for change in diff.iter_all_changes() {
7451 let value = change.value();
7452 match change.tag() {
7453 ChangeTag::Equal => {
7454 offset += value.len();
7455 moved_since_edit = true;
7456 }
7457 ChangeTag::Delete => {
7458 let start = buffer.anchor_after(offset);
7459 let end = buffer.anchor_before(offset + value.len());
7460
7461 if moved_since_edit {
7462 edits.push((start..end, String::new()));
7463 } else {
7464 edits.last_mut().unwrap().0.end = end;
7465 }
7466
7467 offset += value.len();
7468 moved_since_edit = false;
7469 }
7470 ChangeTag::Insert => {
7471 if moved_since_edit {
7472 let anchor = buffer.anchor_after(offset);
7473 edits.push((anchor..anchor, value.to_string()));
7474 } else {
7475 edits.last_mut().unwrap().1.push_str(value);
7476 }
7477
7478 moved_since_edit = false;
7479 }
7480 }
7481 }
7482
7483 rewrapped_row_ranges.push(start_row..=end_row);
7484 }
7485
7486 self.buffer
7487 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
7488 }
7489
7490 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
7491 let mut text = String::new();
7492 let buffer = self.buffer.read(cx).snapshot(cx);
7493 let mut selections = self.selections.all::<Point>(cx);
7494 let mut clipboard_selections = Vec::with_capacity(selections.len());
7495 {
7496 let max_point = buffer.max_point();
7497 let mut is_first = true;
7498 for selection in &mut selections {
7499 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7500 if is_entire_line {
7501 selection.start = Point::new(selection.start.row, 0);
7502 if !selection.is_empty() && selection.end.column == 0 {
7503 selection.end = cmp::min(max_point, selection.end);
7504 } else {
7505 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
7506 }
7507 selection.goal = SelectionGoal::None;
7508 }
7509 if is_first {
7510 is_first = false;
7511 } else {
7512 text += "\n";
7513 }
7514 let mut len = 0;
7515 for chunk in buffer.text_for_range(selection.start..selection.end) {
7516 text.push_str(chunk);
7517 len += chunk.len();
7518 }
7519 clipboard_selections.push(ClipboardSelection {
7520 len,
7521 is_entire_line,
7522 first_line_indent: buffer
7523 .indent_size_for_line(MultiBufferRow(selection.start.row))
7524 .len,
7525 });
7526 }
7527 }
7528
7529 self.transact(window, cx, |this, window, cx| {
7530 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7531 s.select(selections);
7532 });
7533 this.insert("", window, cx);
7534 });
7535 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
7536 }
7537
7538 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
7539 let item = self.cut_common(window, cx);
7540 cx.write_to_clipboard(item);
7541 }
7542
7543 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
7544 self.change_selections(None, window, cx, |s| {
7545 s.move_with(|snapshot, sel| {
7546 if sel.is_empty() {
7547 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
7548 }
7549 });
7550 });
7551 let item = self.cut_common(window, cx);
7552 cx.set_global(KillRing(item))
7553 }
7554
7555 pub fn kill_ring_yank(
7556 &mut self,
7557 _: &KillRingYank,
7558 window: &mut Window,
7559 cx: &mut Context<Self>,
7560 ) {
7561 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
7562 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
7563 (kill_ring.text().to_string(), kill_ring.metadata_json())
7564 } else {
7565 return;
7566 }
7567 } else {
7568 return;
7569 };
7570 self.do_paste(&text, metadata, false, window, cx);
7571 }
7572
7573 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
7574 let selections = self.selections.all::<Point>(cx);
7575 let buffer = self.buffer.read(cx).read(cx);
7576 let mut text = String::new();
7577
7578 let mut clipboard_selections = Vec::with_capacity(selections.len());
7579 {
7580 let max_point = buffer.max_point();
7581 let mut is_first = true;
7582 for selection in selections.iter() {
7583 let mut start = selection.start;
7584 let mut end = selection.end;
7585 let is_entire_line = selection.is_empty() || self.selections.line_mode;
7586 if is_entire_line {
7587 start = Point::new(start.row, 0);
7588 end = cmp::min(max_point, Point::new(end.row + 1, 0));
7589 }
7590 if is_first {
7591 is_first = false;
7592 } else {
7593 text += "\n";
7594 }
7595 let mut len = 0;
7596 for chunk in buffer.text_for_range(start..end) {
7597 text.push_str(chunk);
7598 len += chunk.len();
7599 }
7600 clipboard_selections.push(ClipboardSelection {
7601 len,
7602 is_entire_line,
7603 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
7604 });
7605 }
7606 }
7607
7608 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
7609 text,
7610 clipboard_selections,
7611 ));
7612 }
7613
7614 pub fn do_paste(
7615 &mut self,
7616 text: &String,
7617 clipboard_selections: Option<Vec<ClipboardSelection>>,
7618 handle_entire_lines: bool,
7619 window: &mut Window,
7620 cx: &mut Context<Self>,
7621 ) {
7622 if self.read_only(cx) {
7623 return;
7624 }
7625
7626 let clipboard_text = Cow::Borrowed(text);
7627
7628 self.transact(window, cx, |this, window, cx| {
7629 if let Some(mut clipboard_selections) = clipboard_selections {
7630 let old_selections = this.selections.all::<usize>(cx);
7631 let all_selections_were_entire_line =
7632 clipboard_selections.iter().all(|s| s.is_entire_line);
7633 let first_selection_indent_column =
7634 clipboard_selections.first().map(|s| s.first_line_indent);
7635 if clipboard_selections.len() != old_selections.len() {
7636 clipboard_selections.drain(..);
7637 }
7638 let cursor_offset = this.selections.last::<usize>(cx).head();
7639 let mut auto_indent_on_paste = true;
7640
7641 this.buffer.update(cx, |buffer, cx| {
7642 let snapshot = buffer.read(cx);
7643 auto_indent_on_paste =
7644 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7645
7646 let mut start_offset = 0;
7647 let mut edits = Vec::new();
7648 let mut original_indent_columns = Vec::new();
7649 for (ix, selection) in old_selections.iter().enumerate() {
7650 let to_insert;
7651 let entire_line;
7652 let original_indent_column;
7653 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7654 let end_offset = start_offset + clipboard_selection.len;
7655 to_insert = &clipboard_text[start_offset..end_offset];
7656 entire_line = clipboard_selection.is_entire_line;
7657 start_offset = end_offset + 1;
7658 original_indent_column = Some(clipboard_selection.first_line_indent);
7659 } else {
7660 to_insert = clipboard_text.as_str();
7661 entire_line = all_selections_were_entire_line;
7662 original_indent_column = first_selection_indent_column
7663 }
7664
7665 // If the corresponding selection was empty when this slice of the
7666 // clipboard text was written, then the entire line containing the
7667 // selection was copied. If this selection is also currently empty,
7668 // then paste the line before the current line of the buffer.
7669 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7670 let column = selection.start.to_point(&snapshot).column as usize;
7671 let line_start = selection.start - column;
7672 line_start..line_start
7673 } else {
7674 selection.range()
7675 };
7676
7677 edits.push((range, to_insert));
7678 original_indent_columns.extend(original_indent_column);
7679 }
7680 drop(snapshot);
7681
7682 buffer.edit(
7683 edits,
7684 if auto_indent_on_paste {
7685 Some(AutoindentMode::Block {
7686 original_indent_columns,
7687 })
7688 } else {
7689 None
7690 },
7691 cx,
7692 );
7693 });
7694
7695 let selections = this.selections.all::<usize>(cx);
7696 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7697 s.select(selections)
7698 });
7699 } else {
7700 this.insert(&clipboard_text, window, cx);
7701 }
7702 });
7703 }
7704
7705 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
7706 if let Some(item) = cx.read_from_clipboard() {
7707 let entries = item.entries();
7708
7709 match entries.first() {
7710 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7711 // of all the pasted entries.
7712 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7713 .do_paste(
7714 clipboard_string.text(),
7715 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7716 true,
7717 window,
7718 cx,
7719 ),
7720 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
7721 }
7722 }
7723 }
7724
7725 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
7726 if self.read_only(cx) {
7727 return;
7728 }
7729
7730 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7731 if let Some((selections, _)) =
7732 self.selection_history.transaction(transaction_id).cloned()
7733 {
7734 self.change_selections(None, window, cx, |s| {
7735 s.select_anchors(selections.to_vec());
7736 });
7737 }
7738 self.request_autoscroll(Autoscroll::fit(), cx);
7739 self.unmark_text(window, cx);
7740 self.refresh_inline_completion(true, false, window, cx);
7741 cx.emit(EditorEvent::Edited { transaction_id });
7742 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7743 }
7744 }
7745
7746 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
7747 if self.read_only(cx) {
7748 return;
7749 }
7750
7751 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7752 if let Some((_, Some(selections))) =
7753 self.selection_history.transaction(transaction_id).cloned()
7754 {
7755 self.change_selections(None, window, cx, |s| {
7756 s.select_anchors(selections.to_vec());
7757 });
7758 }
7759 self.request_autoscroll(Autoscroll::fit(), cx);
7760 self.unmark_text(window, cx);
7761 self.refresh_inline_completion(true, false, window, cx);
7762 cx.emit(EditorEvent::Edited { transaction_id });
7763 }
7764 }
7765
7766 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
7767 self.buffer
7768 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7769 }
7770
7771 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
7772 self.buffer
7773 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7774 }
7775
7776 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
7777 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7778 let line_mode = s.line_mode;
7779 s.move_with(|map, selection| {
7780 let cursor = if selection.is_empty() && !line_mode {
7781 movement::left(map, selection.start)
7782 } else {
7783 selection.start
7784 };
7785 selection.collapse_to(cursor, SelectionGoal::None);
7786 });
7787 })
7788 }
7789
7790 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
7791 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7792 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7793 })
7794 }
7795
7796 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
7797 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7798 let line_mode = s.line_mode;
7799 s.move_with(|map, selection| {
7800 let cursor = if selection.is_empty() && !line_mode {
7801 movement::right(map, selection.end)
7802 } else {
7803 selection.end
7804 };
7805 selection.collapse_to(cursor, SelectionGoal::None)
7806 });
7807 })
7808 }
7809
7810 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
7811 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7812 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7813 })
7814 }
7815
7816 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
7817 if self.take_rename(true, window, cx).is_some() {
7818 return;
7819 }
7820
7821 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7822 cx.propagate();
7823 return;
7824 }
7825
7826 let text_layout_details = &self.text_layout_details(window);
7827 let selection_count = self.selections.count();
7828 let first_selection = self.selections.first_anchor();
7829
7830 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7831 let line_mode = s.line_mode;
7832 s.move_with(|map, selection| {
7833 if !selection.is_empty() && !line_mode {
7834 selection.goal = SelectionGoal::None;
7835 }
7836 let (cursor, goal) = movement::up(
7837 map,
7838 selection.start,
7839 selection.goal,
7840 false,
7841 text_layout_details,
7842 );
7843 selection.collapse_to(cursor, goal);
7844 });
7845 });
7846
7847 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7848 {
7849 cx.propagate();
7850 }
7851 }
7852
7853 pub fn move_up_by_lines(
7854 &mut self,
7855 action: &MoveUpByLines,
7856 window: &mut Window,
7857 cx: &mut Context<Self>,
7858 ) {
7859 if self.take_rename(true, window, cx).is_some() {
7860 return;
7861 }
7862
7863 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7864 cx.propagate();
7865 return;
7866 }
7867
7868 let text_layout_details = &self.text_layout_details(window);
7869
7870 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7871 let line_mode = s.line_mode;
7872 s.move_with(|map, selection| {
7873 if !selection.is_empty() && !line_mode {
7874 selection.goal = SelectionGoal::None;
7875 }
7876 let (cursor, goal) = movement::up_by_rows(
7877 map,
7878 selection.start,
7879 action.lines,
7880 selection.goal,
7881 false,
7882 text_layout_details,
7883 );
7884 selection.collapse_to(cursor, goal);
7885 });
7886 })
7887 }
7888
7889 pub fn move_down_by_lines(
7890 &mut self,
7891 action: &MoveDownByLines,
7892 window: &mut Window,
7893 cx: &mut Context<Self>,
7894 ) {
7895 if self.take_rename(true, window, cx).is_some() {
7896 return;
7897 }
7898
7899 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7900 cx.propagate();
7901 return;
7902 }
7903
7904 let text_layout_details = &self.text_layout_details(window);
7905
7906 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7907 let line_mode = s.line_mode;
7908 s.move_with(|map, selection| {
7909 if !selection.is_empty() && !line_mode {
7910 selection.goal = SelectionGoal::None;
7911 }
7912 let (cursor, goal) = movement::down_by_rows(
7913 map,
7914 selection.start,
7915 action.lines,
7916 selection.goal,
7917 false,
7918 text_layout_details,
7919 );
7920 selection.collapse_to(cursor, goal);
7921 });
7922 })
7923 }
7924
7925 pub fn select_down_by_lines(
7926 &mut self,
7927 action: &SelectDownByLines,
7928 window: &mut Window,
7929 cx: &mut Context<Self>,
7930 ) {
7931 let text_layout_details = &self.text_layout_details(window);
7932 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7933 s.move_heads_with(|map, head, goal| {
7934 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7935 })
7936 })
7937 }
7938
7939 pub fn select_up_by_lines(
7940 &mut self,
7941 action: &SelectUpByLines,
7942 window: &mut Window,
7943 cx: &mut Context<Self>,
7944 ) {
7945 let text_layout_details = &self.text_layout_details(window);
7946 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7947 s.move_heads_with(|map, head, goal| {
7948 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7949 })
7950 })
7951 }
7952
7953 pub fn select_page_up(
7954 &mut self,
7955 _: &SelectPageUp,
7956 window: &mut Window,
7957 cx: &mut Context<Self>,
7958 ) {
7959 let Some(row_count) = self.visible_row_count() else {
7960 return;
7961 };
7962
7963 let text_layout_details = &self.text_layout_details(window);
7964
7965 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7966 s.move_heads_with(|map, head, goal| {
7967 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7968 })
7969 })
7970 }
7971
7972 pub fn move_page_up(
7973 &mut self,
7974 action: &MovePageUp,
7975 window: &mut Window,
7976 cx: &mut Context<Self>,
7977 ) {
7978 if self.take_rename(true, window, cx).is_some() {
7979 return;
7980 }
7981
7982 if self
7983 .context_menu
7984 .borrow_mut()
7985 .as_mut()
7986 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7987 .unwrap_or(false)
7988 {
7989 return;
7990 }
7991
7992 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7993 cx.propagate();
7994 return;
7995 }
7996
7997 let Some(row_count) = self.visible_row_count() else {
7998 return;
7999 };
8000
8001 let autoscroll = if action.center_cursor {
8002 Autoscroll::center()
8003 } else {
8004 Autoscroll::fit()
8005 };
8006
8007 let text_layout_details = &self.text_layout_details(window);
8008
8009 self.change_selections(Some(autoscroll), window, cx, |s| {
8010 let line_mode = s.line_mode;
8011 s.move_with(|map, selection| {
8012 if !selection.is_empty() && !line_mode {
8013 selection.goal = SelectionGoal::None;
8014 }
8015 let (cursor, goal) = movement::up_by_rows(
8016 map,
8017 selection.end,
8018 row_count,
8019 selection.goal,
8020 false,
8021 text_layout_details,
8022 );
8023 selection.collapse_to(cursor, goal);
8024 });
8025 });
8026 }
8027
8028 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
8029 let text_layout_details = &self.text_layout_details(window);
8030 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8031 s.move_heads_with(|map, head, goal| {
8032 movement::up(map, head, goal, false, text_layout_details)
8033 })
8034 })
8035 }
8036
8037 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
8038 self.take_rename(true, window, cx);
8039
8040 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8041 cx.propagate();
8042 return;
8043 }
8044
8045 let text_layout_details = &self.text_layout_details(window);
8046 let selection_count = self.selections.count();
8047 let first_selection = self.selections.first_anchor();
8048
8049 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8050 let line_mode = s.line_mode;
8051 s.move_with(|map, selection| {
8052 if !selection.is_empty() && !line_mode {
8053 selection.goal = SelectionGoal::None;
8054 }
8055 let (cursor, goal) = movement::down(
8056 map,
8057 selection.end,
8058 selection.goal,
8059 false,
8060 text_layout_details,
8061 );
8062 selection.collapse_to(cursor, goal);
8063 });
8064 });
8065
8066 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
8067 {
8068 cx.propagate();
8069 }
8070 }
8071
8072 pub fn select_page_down(
8073 &mut self,
8074 _: &SelectPageDown,
8075 window: &mut Window,
8076 cx: &mut Context<Self>,
8077 ) {
8078 let Some(row_count) = self.visible_row_count() else {
8079 return;
8080 };
8081
8082 let text_layout_details = &self.text_layout_details(window);
8083
8084 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8085 s.move_heads_with(|map, head, goal| {
8086 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
8087 })
8088 })
8089 }
8090
8091 pub fn move_page_down(
8092 &mut self,
8093 action: &MovePageDown,
8094 window: &mut Window,
8095 cx: &mut Context<Self>,
8096 ) {
8097 if self.take_rename(true, window, cx).is_some() {
8098 return;
8099 }
8100
8101 if self
8102 .context_menu
8103 .borrow_mut()
8104 .as_mut()
8105 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
8106 .unwrap_or(false)
8107 {
8108 return;
8109 }
8110
8111 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8112 cx.propagate();
8113 return;
8114 }
8115
8116 let Some(row_count) = self.visible_row_count() else {
8117 return;
8118 };
8119
8120 let autoscroll = if action.center_cursor {
8121 Autoscroll::center()
8122 } else {
8123 Autoscroll::fit()
8124 };
8125
8126 let text_layout_details = &self.text_layout_details(window);
8127 self.change_selections(Some(autoscroll), window, cx, |s| {
8128 let line_mode = s.line_mode;
8129 s.move_with(|map, selection| {
8130 if !selection.is_empty() && !line_mode {
8131 selection.goal = SelectionGoal::None;
8132 }
8133 let (cursor, goal) = movement::down_by_rows(
8134 map,
8135 selection.end,
8136 row_count,
8137 selection.goal,
8138 false,
8139 text_layout_details,
8140 );
8141 selection.collapse_to(cursor, goal);
8142 });
8143 });
8144 }
8145
8146 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
8147 let text_layout_details = &self.text_layout_details(window);
8148 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8149 s.move_heads_with(|map, head, goal| {
8150 movement::down(map, head, goal, false, text_layout_details)
8151 })
8152 });
8153 }
8154
8155 pub fn context_menu_first(
8156 &mut self,
8157 _: &ContextMenuFirst,
8158 _window: &mut Window,
8159 cx: &mut Context<Self>,
8160 ) {
8161 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8162 context_menu.select_first(self.completion_provider.as_deref(), cx);
8163 }
8164 }
8165
8166 pub fn context_menu_prev(
8167 &mut self,
8168 _: &ContextMenuPrev,
8169 _window: &mut Window,
8170 cx: &mut Context<Self>,
8171 ) {
8172 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8173 context_menu.select_prev(self.completion_provider.as_deref(), cx);
8174 }
8175 }
8176
8177 pub fn context_menu_next(
8178 &mut self,
8179 _: &ContextMenuNext,
8180 _window: &mut Window,
8181 cx: &mut Context<Self>,
8182 ) {
8183 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8184 context_menu.select_next(self.completion_provider.as_deref(), cx);
8185 }
8186 }
8187
8188 pub fn context_menu_last(
8189 &mut self,
8190 _: &ContextMenuLast,
8191 _window: &mut Window,
8192 cx: &mut Context<Self>,
8193 ) {
8194 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
8195 context_menu.select_last(self.completion_provider.as_deref(), cx);
8196 }
8197 }
8198
8199 pub fn move_to_previous_word_start(
8200 &mut self,
8201 _: &MoveToPreviousWordStart,
8202 window: &mut Window,
8203 cx: &mut Context<Self>,
8204 ) {
8205 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8206 s.move_cursors_with(|map, head, _| {
8207 (
8208 movement::previous_word_start(map, head),
8209 SelectionGoal::None,
8210 )
8211 });
8212 })
8213 }
8214
8215 pub fn move_to_previous_subword_start(
8216 &mut self,
8217 _: &MoveToPreviousSubwordStart,
8218 window: &mut Window,
8219 cx: &mut Context<Self>,
8220 ) {
8221 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8222 s.move_cursors_with(|map, head, _| {
8223 (
8224 movement::previous_subword_start(map, head),
8225 SelectionGoal::None,
8226 )
8227 });
8228 })
8229 }
8230
8231 pub fn select_to_previous_word_start(
8232 &mut self,
8233 _: &SelectToPreviousWordStart,
8234 window: &mut Window,
8235 cx: &mut Context<Self>,
8236 ) {
8237 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8238 s.move_heads_with(|map, head, _| {
8239 (
8240 movement::previous_word_start(map, head),
8241 SelectionGoal::None,
8242 )
8243 });
8244 })
8245 }
8246
8247 pub fn select_to_previous_subword_start(
8248 &mut self,
8249 _: &SelectToPreviousSubwordStart,
8250 window: &mut Window,
8251 cx: &mut Context<Self>,
8252 ) {
8253 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8254 s.move_heads_with(|map, head, _| {
8255 (
8256 movement::previous_subword_start(map, head),
8257 SelectionGoal::None,
8258 )
8259 });
8260 })
8261 }
8262
8263 pub fn delete_to_previous_word_start(
8264 &mut self,
8265 action: &DeleteToPreviousWordStart,
8266 window: &mut Window,
8267 cx: &mut Context<Self>,
8268 ) {
8269 self.transact(window, cx, |this, window, cx| {
8270 this.select_autoclose_pair(window, cx);
8271 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8272 let line_mode = s.line_mode;
8273 s.move_with(|map, selection| {
8274 if selection.is_empty() && !line_mode {
8275 let cursor = if action.ignore_newlines {
8276 movement::previous_word_start(map, selection.head())
8277 } else {
8278 movement::previous_word_start_or_newline(map, selection.head())
8279 };
8280 selection.set_head(cursor, SelectionGoal::None);
8281 }
8282 });
8283 });
8284 this.insert("", window, cx);
8285 });
8286 }
8287
8288 pub fn delete_to_previous_subword_start(
8289 &mut self,
8290 _: &DeleteToPreviousSubwordStart,
8291 window: &mut Window,
8292 cx: &mut Context<Self>,
8293 ) {
8294 self.transact(window, cx, |this, window, cx| {
8295 this.select_autoclose_pair(window, cx);
8296 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8297 let line_mode = s.line_mode;
8298 s.move_with(|map, selection| {
8299 if selection.is_empty() && !line_mode {
8300 let cursor = movement::previous_subword_start(map, selection.head());
8301 selection.set_head(cursor, SelectionGoal::None);
8302 }
8303 });
8304 });
8305 this.insert("", window, cx);
8306 });
8307 }
8308
8309 pub fn move_to_next_word_end(
8310 &mut self,
8311 _: &MoveToNextWordEnd,
8312 window: &mut Window,
8313 cx: &mut Context<Self>,
8314 ) {
8315 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8316 s.move_cursors_with(|map, head, _| {
8317 (movement::next_word_end(map, head), SelectionGoal::None)
8318 });
8319 })
8320 }
8321
8322 pub fn move_to_next_subword_end(
8323 &mut self,
8324 _: &MoveToNextSubwordEnd,
8325 window: &mut Window,
8326 cx: &mut Context<Self>,
8327 ) {
8328 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8329 s.move_cursors_with(|map, head, _| {
8330 (movement::next_subword_end(map, head), SelectionGoal::None)
8331 });
8332 })
8333 }
8334
8335 pub fn select_to_next_word_end(
8336 &mut self,
8337 _: &SelectToNextWordEnd,
8338 window: &mut Window,
8339 cx: &mut Context<Self>,
8340 ) {
8341 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8342 s.move_heads_with(|map, head, _| {
8343 (movement::next_word_end(map, head), SelectionGoal::None)
8344 });
8345 })
8346 }
8347
8348 pub fn select_to_next_subword_end(
8349 &mut self,
8350 _: &SelectToNextSubwordEnd,
8351 window: &mut Window,
8352 cx: &mut Context<Self>,
8353 ) {
8354 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8355 s.move_heads_with(|map, head, _| {
8356 (movement::next_subword_end(map, head), SelectionGoal::None)
8357 });
8358 })
8359 }
8360
8361 pub fn delete_to_next_word_end(
8362 &mut self,
8363 action: &DeleteToNextWordEnd,
8364 window: &mut Window,
8365 cx: &mut Context<Self>,
8366 ) {
8367 self.transact(window, cx, |this, window, cx| {
8368 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8369 let line_mode = s.line_mode;
8370 s.move_with(|map, selection| {
8371 if selection.is_empty() && !line_mode {
8372 let cursor = if action.ignore_newlines {
8373 movement::next_word_end(map, selection.head())
8374 } else {
8375 movement::next_word_end_or_newline(map, selection.head())
8376 };
8377 selection.set_head(cursor, SelectionGoal::None);
8378 }
8379 });
8380 });
8381 this.insert("", window, cx);
8382 });
8383 }
8384
8385 pub fn delete_to_next_subword_end(
8386 &mut self,
8387 _: &DeleteToNextSubwordEnd,
8388 window: &mut Window,
8389 cx: &mut Context<Self>,
8390 ) {
8391 self.transact(window, cx, |this, window, cx| {
8392 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8393 s.move_with(|map, selection| {
8394 if selection.is_empty() {
8395 let cursor = movement::next_subword_end(map, selection.head());
8396 selection.set_head(cursor, SelectionGoal::None);
8397 }
8398 });
8399 });
8400 this.insert("", window, cx);
8401 });
8402 }
8403
8404 pub fn move_to_beginning_of_line(
8405 &mut self,
8406 action: &MoveToBeginningOfLine,
8407 window: &mut Window,
8408 cx: &mut Context<Self>,
8409 ) {
8410 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8411 s.move_cursors_with(|map, head, _| {
8412 (
8413 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8414 SelectionGoal::None,
8415 )
8416 });
8417 })
8418 }
8419
8420 pub fn select_to_beginning_of_line(
8421 &mut self,
8422 action: &SelectToBeginningOfLine,
8423 window: &mut Window,
8424 cx: &mut Context<Self>,
8425 ) {
8426 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8427 s.move_heads_with(|map, head, _| {
8428 (
8429 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
8430 SelectionGoal::None,
8431 )
8432 });
8433 });
8434 }
8435
8436 pub fn delete_to_beginning_of_line(
8437 &mut self,
8438 _: &DeleteToBeginningOfLine,
8439 window: &mut Window,
8440 cx: &mut Context<Self>,
8441 ) {
8442 self.transact(window, cx, |this, window, cx| {
8443 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8444 s.move_with(|_, selection| {
8445 selection.reversed = true;
8446 });
8447 });
8448
8449 this.select_to_beginning_of_line(
8450 &SelectToBeginningOfLine {
8451 stop_at_soft_wraps: false,
8452 },
8453 window,
8454 cx,
8455 );
8456 this.backspace(&Backspace, window, cx);
8457 });
8458 }
8459
8460 pub fn move_to_end_of_line(
8461 &mut self,
8462 action: &MoveToEndOfLine,
8463 window: &mut Window,
8464 cx: &mut Context<Self>,
8465 ) {
8466 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8467 s.move_cursors_with(|map, head, _| {
8468 (
8469 movement::line_end(map, head, action.stop_at_soft_wraps),
8470 SelectionGoal::None,
8471 )
8472 });
8473 })
8474 }
8475
8476 pub fn select_to_end_of_line(
8477 &mut self,
8478 action: &SelectToEndOfLine,
8479 window: &mut Window,
8480 cx: &mut Context<Self>,
8481 ) {
8482 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8483 s.move_heads_with(|map, head, _| {
8484 (
8485 movement::line_end(map, head, action.stop_at_soft_wraps),
8486 SelectionGoal::None,
8487 )
8488 });
8489 })
8490 }
8491
8492 pub fn delete_to_end_of_line(
8493 &mut self,
8494 _: &DeleteToEndOfLine,
8495 window: &mut Window,
8496 cx: &mut Context<Self>,
8497 ) {
8498 self.transact(window, cx, |this, window, cx| {
8499 this.select_to_end_of_line(
8500 &SelectToEndOfLine {
8501 stop_at_soft_wraps: false,
8502 },
8503 window,
8504 cx,
8505 );
8506 this.delete(&Delete, window, cx);
8507 });
8508 }
8509
8510 pub fn cut_to_end_of_line(
8511 &mut self,
8512 _: &CutToEndOfLine,
8513 window: &mut Window,
8514 cx: &mut Context<Self>,
8515 ) {
8516 self.transact(window, cx, |this, window, cx| {
8517 this.select_to_end_of_line(
8518 &SelectToEndOfLine {
8519 stop_at_soft_wraps: false,
8520 },
8521 window,
8522 cx,
8523 );
8524 this.cut(&Cut, window, cx);
8525 });
8526 }
8527
8528 pub fn move_to_start_of_paragraph(
8529 &mut self,
8530 _: &MoveToStartOfParagraph,
8531 window: &mut Window,
8532 cx: &mut Context<Self>,
8533 ) {
8534 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8535 cx.propagate();
8536 return;
8537 }
8538
8539 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8540 s.move_with(|map, selection| {
8541 selection.collapse_to(
8542 movement::start_of_paragraph(map, selection.head(), 1),
8543 SelectionGoal::None,
8544 )
8545 });
8546 })
8547 }
8548
8549 pub fn move_to_end_of_paragraph(
8550 &mut self,
8551 _: &MoveToEndOfParagraph,
8552 window: &mut Window,
8553 cx: &mut Context<Self>,
8554 ) {
8555 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8556 cx.propagate();
8557 return;
8558 }
8559
8560 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8561 s.move_with(|map, selection| {
8562 selection.collapse_to(
8563 movement::end_of_paragraph(map, selection.head(), 1),
8564 SelectionGoal::None,
8565 )
8566 });
8567 })
8568 }
8569
8570 pub fn select_to_start_of_paragraph(
8571 &mut self,
8572 _: &SelectToStartOfParagraph,
8573 window: &mut Window,
8574 cx: &mut Context<Self>,
8575 ) {
8576 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8577 cx.propagate();
8578 return;
8579 }
8580
8581 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8582 s.move_heads_with(|map, head, _| {
8583 (
8584 movement::start_of_paragraph(map, head, 1),
8585 SelectionGoal::None,
8586 )
8587 });
8588 })
8589 }
8590
8591 pub fn select_to_end_of_paragraph(
8592 &mut self,
8593 _: &SelectToEndOfParagraph,
8594 window: &mut Window,
8595 cx: &mut Context<Self>,
8596 ) {
8597 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8598 cx.propagate();
8599 return;
8600 }
8601
8602 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8603 s.move_heads_with(|map, head, _| {
8604 (
8605 movement::end_of_paragraph(map, head, 1),
8606 SelectionGoal::None,
8607 )
8608 });
8609 })
8610 }
8611
8612 pub fn move_to_beginning(
8613 &mut self,
8614 _: &MoveToBeginning,
8615 window: &mut Window,
8616 cx: &mut Context<Self>,
8617 ) {
8618 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8619 cx.propagate();
8620 return;
8621 }
8622
8623 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8624 s.select_ranges(vec![0..0]);
8625 });
8626 }
8627
8628 pub fn select_to_beginning(
8629 &mut self,
8630 _: &SelectToBeginning,
8631 window: &mut Window,
8632 cx: &mut Context<Self>,
8633 ) {
8634 let mut selection = self.selections.last::<Point>(cx);
8635 selection.set_head(Point::zero(), SelectionGoal::None);
8636
8637 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8638 s.select(vec![selection]);
8639 });
8640 }
8641
8642 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
8643 if matches!(self.mode, EditorMode::SingleLine { .. }) {
8644 cx.propagate();
8645 return;
8646 }
8647
8648 let cursor = self.buffer.read(cx).read(cx).len();
8649 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8650 s.select_ranges(vec![cursor..cursor])
8651 });
8652 }
8653
8654 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
8655 self.nav_history = nav_history;
8656 }
8657
8658 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
8659 self.nav_history.as_ref()
8660 }
8661
8662 fn push_to_nav_history(
8663 &mut self,
8664 cursor_anchor: Anchor,
8665 new_position: Option<Point>,
8666 cx: &mut Context<Self>,
8667 ) {
8668 if let Some(nav_history) = self.nav_history.as_mut() {
8669 let buffer = self.buffer.read(cx).read(cx);
8670 let cursor_position = cursor_anchor.to_point(&buffer);
8671 let scroll_state = self.scroll_manager.anchor();
8672 let scroll_top_row = scroll_state.top_row(&buffer);
8673 drop(buffer);
8674
8675 if let Some(new_position) = new_position {
8676 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
8677 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
8678 return;
8679 }
8680 }
8681
8682 nav_history.push(
8683 Some(NavigationData {
8684 cursor_anchor,
8685 cursor_position,
8686 scroll_anchor: scroll_state,
8687 scroll_top_row,
8688 }),
8689 cx,
8690 );
8691 }
8692 }
8693
8694 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
8695 let buffer = self.buffer.read(cx).snapshot(cx);
8696 let mut selection = self.selections.first::<usize>(cx);
8697 selection.set_head(buffer.len(), SelectionGoal::None);
8698 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8699 s.select(vec![selection]);
8700 });
8701 }
8702
8703 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
8704 let end = self.buffer.read(cx).read(cx).len();
8705 self.change_selections(None, window, cx, |s| {
8706 s.select_ranges(vec![0..end]);
8707 });
8708 }
8709
8710 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
8711 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8712 let mut selections = self.selections.all::<Point>(cx);
8713 let max_point = display_map.buffer_snapshot.max_point();
8714 for selection in &mut selections {
8715 let rows = selection.spanned_rows(true, &display_map);
8716 selection.start = Point::new(rows.start.0, 0);
8717 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
8718 selection.reversed = false;
8719 }
8720 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8721 s.select(selections);
8722 });
8723 }
8724
8725 pub fn split_selection_into_lines(
8726 &mut self,
8727 _: &SplitSelectionIntoLines,
8728 window: &mut Window,
8729 cx: &mut Context<Self>,
8730 ) {
8731 let mut to_unfold = Vec::new();
8732 let mut new_selection_ranges = Vec::new();
8733 {
8734 let selections = self.selections.all::<Point>(cx);
8735 let buffer = self.buffer.read(cx).read(cx);
8736 for selection in selections {
8737 for row in selection.start.row..selection.end.row {
8738 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8739 new_selection_ranges.push(cursor..cursor);
8740 }
8741 new_selection_ranges.push(selection.end..selection.end);
8742 to_unfold.push(selection.start..selection.end);
8743 }
8744 }
8745 self.unfold_ranges(&to_unfold, true, true, cx);
8746 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8747 s.select_ranges(new_selection_ranges);
8748 });
8749 }
8750
8751 pub fn add_selection_above(
8752 &mut self,
8753 _: &AddSelectionAbove,
8754 window: &mut Window,
8755 cx: &mut Context<Self>,
8756 ) {
8757 self.add_selection(true, window, cx);
8758 }
8759
8760 pub fn add_selection_below(
8761 &mut self,
8762 _: &AddSelectionBelow,
8763 window: &mut Window,
8764 cx: &mut Context<Self>,
8765 ) {
8766 self.add_selection(false, window, cx);
8767 }
8768
8769 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
8770 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8771 let mut selections = self.selections.all::<Point>(cx);
8772 let text_layout_details = self.text_layout_details(window);
8773 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8774 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8775 let range = oldest_selection.display_range(&display_map).sorted();
8776
8777 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8778 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8779 let positions = start_x.min(end_x)..start_x.max(end_x);
8780
8781 selections.clear();
8782 let mut stack = Vec::new();
8783 for row in range.start.row().0..=range.end.row().0 {
8784 if let Some(selection) = self.selections.build_columnar_selection(
8785 &display_map,
8786 DisplayRow(row),
8787 &positions,
8788 oldest_selection.reversed,
8789 &text_layout_details,
8790 ) {
8791 stack.push(selection.id);
8792 selections.push(selection);
8793 }
8794 }
8795
8796 if above {
8797 stack.reverse();
8798 }
8799
8800 AddSelectionsState { above, stack }
8801 });
8802
8803 let last_added_selection = *state.stack.last().unwrap();
8804 let mut new_selections = Vec::new();
8805 if above == state.above {
8806 let end_row = if above {
8807 DisplayRow(0)
8808 } else {
8809 display_map.max_point().row()
8810 };
8811
8812 'outer: for selection in selections {
8813 if selection.id == last_added_selection {
8814 let range = selection.display_range(&display_map).sorted();
8815 debug_assert_eq!(range.start.row(), range.end.row());
8816 let mut row = range.start.row();
8817 let positions =
8818 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
8819 px(start)..px(end)
8820 } else {
8821 let start_x =
8822 display_map.x_for_display_point(range.start, &text_layout_details);
8823 let end_x =
8824 display_map.x_for_display_point(range.end, &text_layout_details);
8825 start_x.min(end_x)..start_x.max(end_x)
8826 };
8827
8828 while row != end_row {
8829 if above {
8830 row.0 -= 1;
8831 } else {
8832 row.0 += 1;
8833 }
8834
8835 if let Some(new_selection) = self.selections.build_columnar_selection(
8836 &display_map,
8837 row,
8838 &positions,
8839 selection.reversed,
8840 &text_layout_details,
8841 ) {
8842 state.stack.push(new_selection.id);
8843 if above {
8844 new_selections.push(new_selection);
8845 new_selections.push(selection);
8846 } else {
8847 new_selections.push(selection);
8848 new_selections.push(new_selection);
8849 }
8850
8851 continue 'outer;
8852 }
8853 }
8854 }
8855
8856 new_selections.push(selection);
8857 }
8858 } else {
8859 new_selections = selections;
8860 new_selections.retain(|s| s.id != last_added_selection);
8861 state.stack.pop();
8862 }
8863
8864 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8865 s.select(new_selections);
8866 });
8867 if state.stack.len() > 1 {
8868 self.add_selections_state = Some(state);
8869 }
8870 }
8871
8872 pub fn select_next_match_internal(
8873 &mut self,
8874 display_map: &DisplaySnapshot,
8875 replace_newest: bool,
8876 autoscroll: Option<Autoscroll>,
8877 window: &mut Window,
8878 cx: &mut Context<Self>,
8879 ) -> Result<()> {
8880 fn select_next_match_ranges(
8881 this: &mut Editor,
8882 range: Range<usize>,
8883 replace_newest: bool,
8884 auto_scroll: Option<Autoscroll>,
8885 window: &mut Window,
8886 cx: &mut Context<Editor>,
8887 ) {
8888 this.unfold_ranges(&[range.clone()], false, true, cx);
8889 this.change_selections(auto_scroll, window, cx, |s| {
8890 if replace_newest {
8891 s.delete(s.newest_anchor().id);
8892 }
8893 s.insert_range(range.clone());
8894 });
8895 }
8896
8897 let buffer = &display_map.buffer_snapshot;
8898 let mut selections = self.selections.all::<usize>(cx);
8899 if let Some(mut select_next_state) = self.select_next_state.take() {
8900 let query = &select_next_state.query;
8901 if !select_next_state.done {
8902 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8903 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8904 let mut next_selected_range = None;
8905
8906 let bytes_after_last_selection =
8907 buffer.bytes_in_range(last_selection.end..buffer.len());
8908 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8909 let query_matches = query
8910 .stream_find_iter(bytes_after_last_selection)
8911 .map(|result| (last_selection.end, result))
8912 .chain(
8913 query
8914 .stream_find_iter(bytes_before_first_selection)
8915 .map(|result| (0, result)),
8916 );
8917
8918 for (start_offset, query_match) in query_matches {
8919 let query_match = query_match.unwrap(); // can only fail due to I/O
8920 let offset_range =
8921 start_offset + query_match.start()..start_offset + query_match.end();
8922 let display_range = offset_range.start.to_display_point(display_map)
8923 ..offset_range.end.to_display_point(display_map);
8924
8925 if !select_next_state.wordwise
8926 || (!movement::is_inside_word(display_map, display_range.start)
8927 && !movement::is_inside_word(display_map, display_range.end))
8928 {
8929 // TODO: This is n^2, because we might check all the selections
8930 if !selections
8931 .iter()
8932 .any(|selection| selection.range().overlaps(&offset_range))
8933 {
8934 next_selected_range = Some(offset_range);
8935 break;
8936 }
8937 }
8938 }
8939
8940 if let Some(next_selected_range) = next_selected_range {
8941 select_next_match_ranges(
8942 self,
8943 next_selected_range,
8944 replace_newest,
8945 autoscroll,
8946 window,
8947 cx,
8948 );
8949 } else {
8950 select_next_state.done = true;
8951 }
8952 }
8953
8954 self.select_next_state = Some(select_next_state);
8955 } else {
8956 let mut only_carets = true;
8957 let mut same_text_selected = true;
8958 let mut selected_text = None;
8959
8960 let mut selections_iter = selections.iter().peekable();
8961 while let Some(selection) = selections_iter.next() {
8962 if selection.start != selection.end {
8963 only_carets = false;
8964 }
8965
8966 if same_text_selected {
8967 if selected_text.is_none() {
8968 selected_text =
8969 Some(buffer.text_for_range(selection.range()).collect::<String>());
8970 }
8971
8972 if let Some(next_selection) = selections_iter.peek() {
8973 if next_selection.range().len() == selection.range().len() {
8974 let next_selected_text = buffer
8975 .text_for_range(next_selection.range())
8976 .collect::<String>();
8977 if Some(next_selected_text) != selected_text {
8978 same_text_selected = false;
8979 selected_text = None;
8980 }
8981 } else {
8982 same_text_selected = false;
8983 selected_text = None;
8984 }
8985 }
8986 }
8987 }
8988
8989 if only_carets {
8990 for selection in &mut selections {
8991 let word_range = movement::surrounding_word(
8992 display_map,
8993 selection.start.to_display_point(display_map),
8994 );
8995 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8996 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8997 selection.goal = SelectionGoal::None;
8998 selection.reversed = false;
8999 select_next_match_ranges(
9000 self,
9001 selection.start..selection.end,
9002 replace_newest,
9003 autoscroll,
9004 window,
9005 cx,
9006 );
9007 }
9008
9009 if selections.len() == 1 {
9010 let selection = selections
9011 .last()
9012 .expect("ensured that there's only one selection");
9013 let query = buffer
9014 .text_for_range(selection.start..selection.end)
9015 .collect::<String>();
9016 let is_empty = query.is_empty();
9017 let select_state = SelectNextState {
9018 query: AhoCorasick::new(&[query])?,
9019 wordwise: true,
9020 done: is_empty,
9021 };
9022 self.select_next_state = Some(select_state);
9023 } else {
9024 self.select_next_state = None;
9025 }
9026 } else if let Some(selected_text) = selected_text {
9027 self.select_next_state = Some(SelectNextState {
9028 query: AhoCorasick::new(&[selected_text])?,
9029 wordwise: false,
9030 done: false,
9031 });
9032 self.select_next_match_internal(
9033 display_map,
9034 replace_newest,
9035 autoscroll,
9036 window,
9037 cx,
9038 )?;
9039 }
9040 }
9041 Ok(())
9042 }
9043
9044 pub fn select_all_matches(
9045 &mut self,
9046 _action: &SelectAllMatches,
9047 window: &mut Window,
9048 cx: &mut Context<Self>,
9049 ) -> Result<()> {
9050 self.push_to_selection_history();
9051 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9052
9053 self.select_next_match_internal(&display_map, false, None, window, cx)?;
9054 let Some(select_next_state) = self.select_next_state.as_mut() else {
9055 return Ok(());
9056 };
9057 if select_next_state.done {
9058 return Ok(());
9059 }
9060
9061 let mut new_selections = self.selections.all::<usize>(cx);
9062
9063 let buffer = &display_map.buffer_snapshot;
9064 let query_matches = select_next_state
9065 .query
9066 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
9067
9068 for query_match in query_matches {
9069 let query_match = query_match.unwrap(); // can only fail due to I/O
9070 let offset_range = query_match.start()..query_match.end();
9071 let display_range = offset_range.start.to_display_point(&display_map)
9072 ..offset_range.end.to_display_point(&display_map);
9073
9074 if !select_next_state.wordwise
9075 || (!movement::is_inside_word(&display_map, display_range.start)
9076 && !movement::is_inside_word(&display_map, display_range.end))
9077 {
9078 self.selections.change_with(cx, |selections| {
9079 new_selections.push(Selection {
9080 id: selections.new_selection_id(),
9081 start: offset_range.start,
9082 end: offset_range.end,
9083 reversed: false,
9084 goal: SelectionGoal::None,
9085 });
9086 });
9087 }
9088 }
9089
9090 new_selections.sort_by_key(|selection| selection.start);
9091 let mut ix = 0;
9092 while ix + 1 < new_selections.len() {
9093 let current_selection = &new_selections[ix];
9094 let next_selection = &new_selections[ix + 1];
9095 if current_selection.range().overlaps(&next_selection.range()) {
9096 if current_selection.id < next_selection.id {
9097 new_selections.remove(ix + 1);
9098 } else {
9099 new_selections.remove(ix);
9100 }
9101 } else {
9102 ix += 1;
9103 }
9104 }
9105
9106 let reversed = self.selections.oldest::<usize>(cx).reversed;
9107
9108 for selection in new_selections.iter_mut() {
9109 selection.reversed = reversed;
9110 }
9111
9112 select_next_state.done = true;
9113 self.unfold_ranges(
9114 &new_selections
9115 .iter()
9116 .map(|selection| selection.range())
9117 .collect::<Vec<_>>(),
9118 false,
9119 false,
9120 cx,
9121 );
9122 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
9123 selections.select(new_selections)
9124 });
9125
9126 Ok(())
9127 }
9128
9129 pub fn select_next(
9130 &mut self,
9131 action: &SelectNext,
9132 window: &mut Window,
9133 cx: &mut Context<Self>,
9134 ) -> Result<()> {
9135 self.push_to_selection_history();
9136 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9137 self.select_next_match_internal(
9138 &display_map,
9139 action.replace_newest,
9140 Some(Autoscroll::newest()),
9141 window,
9142 cx,
9143 )?;
9144 Ok(())
9145 }
9146
9147 pub fn select_previous(
9148 &mut self,
9149 action: &SelectPrevious,
9150 window: &mut Window,
9151 cx: &mut Context<Self>,
9152 ) -> Result<()> {
9153 self.push_to_selection_history();
9154 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9155 let buffer = &display_map.buffer_snapshot;
9156 let mut selections = self.selections.all::<usize>(cx);
9157 if let Some(mut select_prev_state) = self.select_prev_state.take() {
9158 let query = &select_prev_state.query;
9159 if !select_prev_state.done {
9160 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
9161 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
9162 let mut next_selected_range = None;
9163 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
9164 let bytes_before_last_selection =
9165 buffer.reversed_bytes_in_range(0..last_selection.start);
9166 let bytes_after_first_selection =
9167 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
9168 let query_matches = query
9169 .stream_find_iter(bytes_before_last_selection)
9170 .map(|result| (last_selection.start, result))
9171 .chain(
9172 query
9173 .stream_find_iter(bytes_after_first_selection)
9174 .map(|result| (buffer.len(), result)),
9175 );
9176 for (end_offset, query_match) in query_matches {
9177 let query_match = query_match.unwrap(); // can only fail due to I/O
9178 let offset_range =
9179 end_offset - query_match.end()..end_offset - query_match.start();
9180 let display_range = offset_range.start.to_display_point(&display_map)
9181 ..offset_range.end.to_display_point(&display_map);
9182
9183 if !select_prev_state.wordwise
9184 || (!movement::is_inside_word(&display_map, display_range.start)
9185 && !movement::is_inside_word(&display_map, display_range.end))
9186 {
9187 next_selected_range = Some(offset_range);
9188 break;
9189 }
9190 }
9191
9192 if let Some(next_selected_range) = next_selected_range {
9193 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
9194 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9195 if action.replace_newest {
9196 s.delete(s.newest_anchor().id);
9197 }
9198 s.insert_range(next_selected_range);
9199 });
9200 } else {
9201 select_prev_state.done = true;
9202 }
9203 }
9204
9205 self.select_prev_state = Some(select_prev_state);
9206 } else {
9207 let mut only_carets = true;
9208 let mut same_text_selected = true;
9209 let mut selected_text = None;
9210
9211 let mut selections_iter = selections.iter().peekable();
9212 while let Some(selection) = selections_iter.next() {
9213 if selection.start != selection.end {
9214 only_carets = false;
9215 }
9216
9217 if same_text_selected {
9218 if selected_text.is_none() {
9219 selected_text =
9220 Some(buffer.text_for_range(selection.range()).collect::<String>());
9221 }
9222
9223 if let Some(next_selection) = selections_iter.peek() {
9224 if next_selection.range().len() == selection.range().len() {
9225 let next_selected_text = buffer
9226 .text_for_range(next_selection.range())
9227 .collect::<String>();
9228 if Some(next_selected_text) != selected_text {
9229 same_text_selected = false;
9230 selected_text = None;
9231 }
9232 } else {
9233 same_text_selected = false;
9234 selected_text = None;
9235 }
9236 }
9237 }
9238 }
9239
9240 if only_carets {
9241 for selection in &mut selections {
9242 let word_range = movement::surrounding_word(
9243 &display_map,
9244 selection.start.to_display_point(&display_map),
9245 );
9246 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
9247 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
9248 selection.goal = SelectionGoal::None;
9249 selection.reversed = false;
9250 }
9251 if selections.len() == 1 {
9252 let selection = selections
9253 .last()
9254 .expect("ensured that there's only one selection");
9255 let query = buffer
9256 .text_for_range(selection.start..selection.end)
9257 .collect::<String>();
9258 let is_empty = query.is_empty();
9259 let select_state = SelectNextState {
9260 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
9261 wordwise: true,
9262 done: is_empty,
9263 };
9264 self.select_prev_state = Some(select_state);
9265 } else {
9266 self.select_prev_state = None;
9267 }
9268
9269 self.unfold_ranges(
9270 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
9271 false,
9272 true,
9273 cx,
9274 );
9275 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
9276 s.select(selections);
9277 });
9278 } else if let Some(selected_text) = selected_text {
9279 self.select_prev_state = Some(SelectNextState {
9280 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
9281 wordwise: false,
9282 done: false,
9283 });
9284 self.select_previous(action, window, cx)?;
9285 }
9286 }
9287 Ok(())
9288 }
9289
9290 pub fn toggle_comments(
9291 &mut self,
9292 action: &ToggleComments,
9293 window: &mut Window,
9294 cx: &mut Context<Self>,
9295 ) {
9296 if self.read_only(cx) {
9297 return;
9298 }
9299 let text_layout_details = &self.text_layout_details(window);
9300 self.transact(window, cx, |this, window, cx| {
9301 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9302 let mut edits = Vec::new();
9303 let mut selection_edit_ranges = Vec::new();
9304 let mut last_toggled_row = None;
9305 let snapshot = this.buffer.read(cx).read(cx);
9306 let empty_str: Arc<str> = Arc::default();
9307 let mut suffixes_inserted = Vec::new();
9308 let ignore_indent = action.ignore_indent;
9309
9310 fn comment_prefix_range(
9311 snapshot: &MultiBufferSnapshot,
9312 row: MultiBufferRow,
9313 comment_prefix: &str,
9314 comment_prefix_whitespace: &str,
9315 ignore_indent: bool,
9316 ) -> Range<Point> {
9317 let indent_size = if ignore_indent {
9318 0
9319 } else {
9320 snapshot.indent_size_for_line(row).len
9321 };
9322
9323 let start = Point::new(row.0, indent_size);
9324
9325 let mut line_bytes = snapshot
9326 .bytes_in_range(start..snapshot.max_point())
9327 .flatten()
9328 .copied();
9329
9330 // If this line currently begins with the line comment prefix, then record
9331 // the range containing the prefix.
9332 if line_bytes
9333 .by_ref()
9334 .take(comment_prefix.len())
9335 .eq(comment_prefix.bytes())
9336 {
9337 // Include any whitespace that matches the comment prefix.
9338 let matching_whitespace_len = line_bytes
9339 .zip(comment_prefix_whitespace.bytes())
9340 .take_while(|(a, b)| a == b)
9341 .count() as u32;
9342 let end = Point::new(
9343 start.row,
9344 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
9345 );
9346 start..end
9347 } else {
9348 start..start
9349 }
9350 }
9351
9352 fn comment_suffix_range(
9353 snapshot: &MultiBufferSnapshot,
9354 row: MultiBufferRow,
9355 comment_suffix: &str,
9356 comment_suffix_has_leading_space: bool,
9357 ) -> Range<Point> {
9358 let end = Point::new(row.0, snapshot.line_len(row));
9359 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
9360
9361 let mut line_end_bytes = snapshot
9362 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
9363 .flatten()
9364 .copied();
9365
9366 let leading_space_len = if suffix_start_column > 0
9367 && line_end_bytes.next() == Some(b' ')
9368 && comment_suffix_has_leading_space
9369 {
9370 1
9371 } else {
9372 0
9373 };
9374
9375 // If this line currently begins with the line comment prefix, then record
9376 // the range containing the prefix.
9377 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
9378 let start = Point::new(end.row, suffix_start_column - leading_space_len);
9379 start..end
9380 } else {
9381 end..end
9382 }
9383 }
9384
9385 // TODO: Handle selections that cross excerpts
9386 for selection in &mut selections {
9387 let start_column = snapshot
9388 .indent_size_for_line(MultiBufferRow(selection.start.row))
9389 .len;
9390 let language = if let Some(language) =
9391 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
9392 {
9393 language
9394 } else {
9395 continue;
9396 };
9397
9398 selection_edit_ranges.clear();
9399
9400 // If multiple selections contain a given row, avoid processing that
9401 // row more than once.
9402 let mut start_row = MultiBufferRow(selection.start.row);
9403 if last_toggled_row == Some(start_row) {
9404 start_row = start_row.next_row();
9405 }
9406 let end_row =
9407 if selection.end.row > selection.start.row && selection.end.column == 0 {
9408 MultiBufferRow(selection.end.row - 1)
9409 } else {
9410 MultiBufferRow(selection.end.row)
9411 };
9412 last_toggled_row = Some(end_row);
9413
9414 if start_row > end_row {
9415 continue;
9416 }
9417
9418 // If the language has line comments, toggle those.
9419 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
9420
9421 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
9422 if ignore_indent {
9423 full_comment_prefixes = full_comment_prefixes
9424 .into_iter()
9425 .map(|s| Arc::from(s.trim_end()))
9426 .collect();
9427 }
9428
9429 if !full_comment_prefixes.is_empty() {
9430 let first_prefix = full_comment_prefixes
9431 .first()
9432 .expect("prefixes is non-empty");
9433 let prefix_trimmed_lengths = full_comment_prefixes
9434 .iter()
9435 .map(|p| p.trim_end_matches(' ').len())
9436 .collect::<SmallVec<[usize; 4]>>();
9437
9438 let mut all_selection_lines_are_comments = true;
9439
9440 for row in start_row.0..=end_row.0 {
9441 let row = MultiBufferRow(row);
9442 if start_row < end_row && snapshot.is_line_blank(row) {
9443 continue;
9444 }
9445
9446 let prefix_range = full_comment_prefixes
9447 .iter()
9448 .zip(prefix_trimmed_lengths.iter().copied())
9449 .map(|(prefix, trimmed_prefix_len)| {
9450 comment_prefix_range(
9451 snapshot.deref(),
9452 row,
9453 &prefix[..trimmed_prefix_len],
9454 &prefix[trimmed_prefix_len..],
9455 ignore_indent,
9456 )
9457 })
9458 .max_by_key(|range| range.end.column - range.start.column)
9459 .expect("prefixes is non-empty");
9460
9461 if prefix_range.is_empty() {
9462 all_selection_lines_are_comments = false;
9463 }
9464
9465 selection_edit_ranges.push(prefix_range);
9466 }
9467
9468 if all_selection_lines_are_comments {
9469 edits.extend(
9470 selection_edit_ranges
9471 .iter()
9472 .cloned()
9473 .map(|range| (range, empty_str.clone())),
9474 );
9475 } else {
9476 let min_column = selection_edit_ranges
9477 .iter()
9478 .map(|range| range.start.column)
9479 .min()
9480 .unwrap_or(0);
9481 edits.extend(selection_edit_ranges.iter().map(|range| {
9482 let position = Point::new(range.start.row, min_column);
9483 (position..position, first_prefix.clone())
9484 }));
9485 }
9486 } else if let Some((full_comment_prefix, comment_suffix)) =
9487 language.block_comment_delimiters()
9488 {
9489 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
9490 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
9491 let prefix_range = comment_prefix_range(
9492 snapshot.deref(),
9493 start_row,
9494 comment_prefix,
9495 comment_prefix_whitespace,
9496 ignore_indent,
9497 );
9498 let suffix_range = comment_suffix_range(
9499 snapshot.deref(),
9500 end_row,
9501 comment_suffix.trim_start_matches(' '),
9502 comment_suffix.starts_with(' '),
9503 );
9504
9505 if prefix_range.is_empty() || suffix_range.is_empty() {
9506 edits.push((
9507 prefix_range.start..prefix_range.start,
9508 full_comment_prefix.clone(),
9509 ));
9510 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
9511 suffixes_inserted.push((end_row, comment_suffix.len()));
9512 } else {
9513 edits.push((prefix_range, empty_str.clone()));
9514 edits.push((suffix_range, empty_str.clone()));
9515 }
9516 } else {
9517 continue;
9518 }
9519 }
9520
9521 drop(snapshot);
9522 this.buffer.update(cx, |buffer, cx| {
9523 buffer.edit(edits, None, cx);
9524 });
9525
9526 // Adjust selections so that they end before any comment suffixes that
9527 // were inserted.
9528 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
9529 let mut selections = this.selections.all::<Point>(cx);
9530 let snapshot = this.buffer.read(cx).read(cx);
9531 for selection in &mut selections {
9532 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
9533 match row.cmp(&MultiBufferRow(selection.end.row)) {
9534 Ordering::Less => {
9535 suffixes_inserted.next();
9536 continue;
9537 }
9538 Ordering::Greater => break,
9539 Ordering::Equal => {
9540 if selection.end.column == snapshot.line_len(row) {
9541 if selection.is_empty() {
9542 selection.start.column -= suffix_len as u32;
9543 }
9544 selection.end.column -= suffix_len as u32;
9545 }
9546 break;
9547 }
9548 }
9549 }
9550 }
9551
9552 drop(snapshot);
9553 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9554 s.select(selections)
9555 });
9556
9557 let selections = this.selections.all::<Point>(cx);
9558 let selections_on_single_row = selections.windows(2).all(|selections| {
9559 selections[0].start.row == selections[1].start.row
9560 && selections[0].end.row == selections[1].end.row
9561 && selections[0].start.row == selections[0].end.row
9562 });
9563 let selections_selecting = selections
9564 .iter()
9565 .any(|selection| selection.start != selection.end);
9566 let advance_downwards = action.advance_downwards
9567 && selections_on_single_row
9568 && !selections_selecting
9569 && !matches!(this.mode, EditorMode::SingleLine { .. });
9570
9571 if advance_downwards {
9572 let snapshot = this.buffer.read(cx).snapshot(cx);
9573
9574 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9575 s.move_cursors_with(|display_snapshot, display_point, _| {
9576 let mut point = display_point.to_point(display_snapshot);
9577 point.row += 1;
9578 point = snapshot.clip_point(point, Bias::Left);
9579 let display_point = point.to_display_point(display_snapshot);
9580 let goal = SelectionGoal::HorizontalPosition(
9581 display_snapshot
9582 .x_for_display_point(display_point, text_layout_details)
9583 .into(),
9584 );
9585 (display_point, goal)
9586 })
9587 });
9588 }
9589 });
9590 }
9591
9592 pub fn select_enclosing_symbol(
9593 &mut self,
9594 _: &SelectEnclosingSymbol,
9595 window: &mut Window,
9596 cx: &mut Context<Self>,
9597 ) {
9598 let buffer = self.buffer.read(cx).snapshot(cx);
9599 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9600
9601 fn update_selection(
9602 selection: &Selection<usize>,
9603 buffer_snap: &MultiBufferSnapshot,
9604 ) -> Option<Selection<usize>> {
9605 let cursor = selection.head();
9606 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
9607 for symbol in symbols.iter().rev() {
9608 let start = symbol.range.start.to_offset(buffer_snap);
9609 let end = symbol.range.end.to_offset(buffer_snap);
9610 let new_range = start..end;
9611 if start < selection.start || end > selection.end {
9612 return Some(Selection {
9613 id: selection.id,
9614 start: new_range.start,
9615 end: new_range.end,
9616 goal: SelectionGoal::None,
9617 reversed: selection.reversed,
9618 });
9619 }
9620 }
9621 None
9622 }
9623
9624 let mut selected_larger_symbol = false;
9625 let new_selections = old_selections
9626 .iter()
9627 .map(|selection| match update_selection(selection, &buffer) {
9628 Some(new_selection) => {
9629 if new_selection.range() != selection.range() {
9630 selected_larger_symbol = true;
9631 }
9632 new_selection
9633 }
9634 None => selection.clone(),
9635 })
9636 .collect::<Vec<_>>();
9637
9638 if selected_larger_symbol {
9639 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9640 s.select(new_selections);
9641 });
9642 }
9643 }
9644
9645 pub fn select_larger_syntax_node(
9646 &mut self,
9647 _: &SelectLargerSyntaxNode,
9648 window: &mut Window,
9649 cx: &mut Context<Self>,
9650 ) {
9651 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9652 let buffer = self.buffer.read(cx).snapshot(cx);
9653 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
9654
9655 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9656 let mut selected_larger_node = false;
9657 let new_selections = old_selections
9658 .iter()
9659 .map(|selection| {
9660 let old_range = selection.start..selection.end;
9661 let mut new_range = old_range.clone();
9662 let mut new_node = None;
9663 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
9664 {
9665 new_node = Some(node);
9666 new_range = containing_range;
9667 if !display_map.intersects_fold(new_range.start)
9668 && !display_map.intersects_fold(new_range.end)
9669 {
9670 break;
9671 }
9672 }
9673
9674 if let Some(node) = new_node {
9675 // Log the ancestor, to support using this action as a way to explore TreeSitter
9676 // nodes. Parent and grandparent are also logged because this operation will not
9677 // visit nodes that have the same range as their parent.
9678 log::info!("Node: {node:?}");
9679 let parent = node.parent();
9680 log::info!("Parent: {parent:?}");
9681 let grandparent = parent.and_then(|x| x.parent());
9682 log::info!("Grandparent: {grandparent:?}");
9683 }
9684
9685 selected_larger_node |= new_range != old_range;
9686 Selection {
9687 id: selection.id,
9688 start: new_range.start,
9689 end: new_range.end,
9690 goal: SelectionGoal::None,
9691 reversed: selection.reversed,
9692 }
9693 })
9694 .collect::<Vec<_>>();
9695
9696 if selected_larger_node {
9697 stack.push(old_selections);
9698 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9699 s.select(new_selections);
9700 });
9701 }
9702 self.select_larger_syntax_node_stack = stack;
9703 }
9704
9705 pub fn select_smaller_syntax_node(
9706 &mut self,
9707 _: &SelectSmallerSyntaxNode,
9708 window: &mut Window,
9709 cx: &mut Context<Self>,
9710 ) {
9711 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
9712 if let Some(selections) = stack.pop() {
9713 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9714 s.select(selections.to_vec());
9715 });
9716 }
9717 self.select_larger_syntax_node_stack = stack;
9718 }
9719
9720 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
9721 if !EditorSettings::get_global(cx).gutter.runnables {
9722 self.clear_tasks();
9723 return Task::ready(());
9724 }
9725 let project = self.project.as_ref().map(Entity::downgrade);
9726 cx.spawn_in(window, |this, mut cx| async move {
9727 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
9728 let Some(project) = project.and_then(|p| p.upgrade()) else {
9729 return;
9730 };
9731 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
9732 this.display_map.update(cx, |map, cx| map.snapshot(cx))
9733 }) else {
9734 return;
9735 };
9736
9737 let hide_runnables = project
9738 .update(&mut cx, |project, cx| {
9739 // Do not display any test indicators in non-dev server remote projects.
9740 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
9741 })
9742 .unwrap_or(true);
9743 if hide_runnables {
9744 return;
9745 }
9746 let new_rows =
9747 cx.background_executor()
9748 .spawn({
9749 let snapshot = display_snapshot.clone();
9750 async move {
9751 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
9752 }
9753 })
9754 .await;
9755
9756 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
9757 this.update(&mut cx, |this, _| {
9758 this.clear_tasks();
9759 for (key, value) in rows {
9760 this.insert_tasks(key, value);
9761 }
9762 })
9763 .ok();
9764 })
9765 }
9766 fn fetch_runnable_ranges(
9767 snapshot: &DisplaySnapshot,
9768 range: Range<Anchor>,
9769 ) -> Vec<language::RunnableRange> {
9770 snapshot.buffer_snapshot.runnable_ranges(range).collect()
9771 }
9772
9773 fn runnable_rows(
9774 project: Entity<Project>,
9775 snapshot: DisplaySnapshot,
9776 runnable_ranges: Vec<RunnableRange>,
9777 mut cx: AsyncWindowContext,
9778 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9779 runnable_ranges
9780 .into_iter()
9781 .filter_map(|mut runnable| {
9782 let tasks = cx
9783 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9784 .ok()?;
9785 if tasks.is_empty() {
9786 return None;
9787 }
9788
9789 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9790
9791 let row = snapshot
9792 .buffer_snapshot
9793 .buffer_line_for_row(MultiBufferRow(point.row))?
9794 .1
9795 .start
9796 .row;
9797
9798 let context_range =
9799 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9800 Some((
9801 (runnable.buffer_id, row),
9802 RunnableTasks {
9803 templates: tasks,
9804 offset: MultiBufferOffset(runnable.run_range.start),
9805 context_range,
9806 column: point.column,
9807 extra_variables: runnable.extra_captures,
9808 },
9809 ))
9810 })
9811 .collect()
9812 }
9813
9814 fn templates_with_tags(
9815 project: &Entity<Project>,
9816 runnable: &mut Runnable,
9817 cx: &mut App,
9818 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9819 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9820 let (worktree_id, file) = project
9821 .buffer_for_id(runnable.buffer, cx)
9822 .and_then(|buffer| buffer.read(cx).file())
9823 .map(|file| (file.worktree_id(cx), file.clone()))
9824 .unzip();
9825
9826 (
9827 project.task_store().read(cx).task_inventory().cloned(),
9828 worktree_id,
9829 file,
9830 )
9831 });
9832
9833 let tags = mem::take(&mut runnable.tags);
9834 let mut tags: Vec<_> = tags
9835 .into_iter()
9836 .flat_map(|tag| {
9837 let tag = tag.0.clone();
9838 inventory
9839 .as_ref()
9840 .into_iter()
9841 .flat_map(|inventory| {
9842 inventory.read(cx).list_tasks(
9843 file.clone(),
9844 Some(runnable.language.clone()),
9845 worktree_id,
9846 cx,
9847 )
9848 })
9849 .filter(move |(_, template)| {
9850 template.tags.iter().any(|source_tag| source_tag == &tag)
9851 })
9852 })
9853 .sorted_by_key(|(kind, _)| kind.to_owned())
9854 .collect();
9855 if let Some((leading_tag_source, _)) = tags.first() {
9856 // Strongest source wins; if we have worktree tag binding, prefer that to
9857 // global and language bindings;
9858 // if we have a global binding, prefer that to language binding.
9859 let first_mismatch = tags
9860 .iter()
9861 .position(|(tag_source, _)| tag_source != leading_tag_source);
9862 if let Some(index) = first_mismatch {
9863 tags.truncate(index);
9864 }
9865 }
9866
9867 tags
9868 }
9869
9870 pub fn move_to_enclosing_bracket(
9871 &mut self,
9872 _: &MoveToEnclosingBracket,
9873 window: &mut Window,
9874 cx: &mut Context<Self>,
9875 ) {
9876 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9877 s.move_offsets_with(|snapshot, selection| {
9878 let Some(enclosing_bracket_ranges) =
9879 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9880 else {
9881 return;
9882 };
9883
9884 let mut best_length = usize::MAX;
9885 let mut best_inside = false;
9886 let mut best_in_bracket_range = false;
9887 let mut best_destination = None;
9888 for (open, close) in enclosing_bracket_ranges {
9889 let close = close.to_inclusive();
9890 let length = close.end() - open.start;
9891 let inside = selection.start >= open.end && selection.end <= *close.start();
9892 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9893 || close.contains(&selection.head());
9894
9895 // If best is next to a bracket and current isn't, skip
9896 if !in_bracket_range && best_in_bracket_range {
9897 continue;
9898 }
9899
9900 // Prefer smaller lengths unless best is inside and current isn't
9901 if length > best_length && (best_inside || !inside) {
9902 continue;
9903 }
9904
9905 best_length = length;
9906 best_inside = inside;
9907 best_in_bracket_range = in_bracket_range;
9908 best_destination = Some(
9909 if close.contains(&selection.start) && close.contains(&selection.end) {
9910 if inside {
9911 open.end
9912 } else {
9913 open.start
9914 }
9915 } else if inside {
9916 *close.start()
9917 } else {
9918 *close.end()
9919 },
9920 );
9921 }
9922
9923 if let Some(destination) = best_destination {
9924 selection.collapse_to(destination, SelectionGoal::None);
9925 }
9926 })
9927 });
9928 }
9929
9930 pub fn undo_selection(
9931 &mut self,
9932 _: &UndoSelection,
9933 window: &mut Window,
9934 cx: &mut Context<Self>,
9935 ) {
9936 self.end_selection(window, cx);
9937 self.selection_history.mode = SelectionHistoryMode::Undoing;
9938 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9939 self.change_selections(None, window, cx, |s| {
9940 s.select_anchors(entry.selections.to_vec())
9941 });
9942 self.select_next_state = entry.select_next_state;
9943 self.select_prev_state = entry.select_prev_state;
9944 self.add_selections_state = entry.add_selections_state;
9945 self.request_autoscroll(Autoscroll::newest(), cx);
9946 }
9947 self.selection_history.mode = SelectionHistoryMode::Normal;
9948 }
9949
9950 pub fn redo_selection(
9951 &mut self,
9952 _: &RedoSelection,
9953 window: &mut Window,
9954 cx: &mut Context<Self>,
9955 ) {
9956 self.end_selection(window, cx);
9957 self.selection_history.mode = SelectionHistoryMode::Redoing;
9958 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9959 self.change_selections(None, window, cx, |s| {
9960 s.select_anchors(entry.selections.to_vec())
9961 });
9962 self.select_next_state = entry.select_next_state;
9963 self.select_prev_state = entry.select_prev_state;
9964 self.add_selections_state = entry.add_selections_state;
9965 self.request_autoscroll(Autoscroll::newest(), cx);
9966 }
9967 self.selection_history.mode = SelectionHistoryMode::Normal;
9968 }
9969
9970 pub fn expand_excerpts(
9971 &mut self,
9972 action: &ExpandExcerpts,
9973 _: &mut Window,
9974 cx: &mut Context<Self>,
9975 ) {
9976 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9977 }
9978
9979 pub fn expand_excerpts_down(
9980 &mut self,
9981 action: &ExpandExcerptsDown,
9982 _: &mut Window,
9983 cx: &mut Context<Self>,
9984 ) {
9985 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9986 }
9987
9988 pub fn expand_excerpts_up(
9989 &mut self,
9990 action: &ExpandExcerptsUp,
9991 _: &mut Window,
9992 cx: &mut Context<Self>,
9993 ) {
9994 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9995 }
9996
9997 pub fn expand_excerpts_for_direction(
9998 &mut self,
9999 lines: u32,
10000 direction: ExpandExcerptDirection,
10001
10002 cx: &mut Context<Self>,
10003 ) {
10004 let selections = self.selections.disjoint_anchors();
10005
10006 let lines = if lines == 0 {
10007 EditorSettings::get_global(cx).expand_excerpt_lines
10008 } else {
10009 lines
10010 };
10011
10012 self.buffer.update(cx, |buffer, cx| {
10013 let snapshot = buffer.snapshot(cx);
10014 let mut excerpt_ids = selections
10015 .iter()
10016 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
10017 .collect::<Vec<_>>();
10018 excerpt_ids.sort();
10019 excerpt_ids.dedup();
10020 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
10021 })
10022 }
10023
10024 pub fn expand_excerpt(
10025 &mut self,
10026 excerpt: ExcerptId,
10027 direction: ExpandExcerptDirection,
10028 cx: &mut Context<Self>,
10029 ) {
10030 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
10031 self.buffer.update(cx, |buffer, cx| {
10032 buffer.expand_excerpts([excerpt], lines, direction, cx)
10033 })
10034 }
10035
10036 pub fn go_to_singleton_buffer_point(
10037 &mut self,
10038 point: Point,
10039 window: &mut Window,
10040 cx: &mut Context<Self>,
10041 ) {
10042 self.go_to_singleton_buffer_range(point..point, window, cx);
10043 }
10044
10045 pub fn go_to_singleton_buffer_range(
10046 &mut self,
10047 range: Range<Point>,
10048 window: &mut Window,
10049 cx: &mut Context<Self>,
10050 ) {
10051 let multibuffer = self.buffer().read(cx);
10052 let Some(buffer) = multibuffer.as_singleton() else {
10053 return;
10054 };
10055 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
10056 return;
10057 };
10058 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
10059 return;
10060 };
10061 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
10062 s.select_anchor_ranges([start..end])
10063 });
10064 }
10065
10066 fn go_to_diagnostic(
10067 &mut self,
10068 _: &GoToDiagnostic,
10069 window: &mut Window,
10070 cx: &mut Context<Self>,
10071 ) {
10072 self.go_to_diagnostic_impl(Direction::Next, window, cx)
10073 }
10074
10075 fn go_to_prev_diagnostic(
10076 &mut self,
10077 _: &GoToPrevDiagnostic,
10078 window: &mut Window,
10079 cx: &mut Context<Self>,
10080 ) {
10081 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
10082 }
10083
10084 pub fn go_to_diagnostic_impl(
10085 &mut self,
10086 direction: Direction,
10087 window: &mut Window,
10088 cx: &mut Context<Self>,
10089 ) {
10090 let buffer = self.buffer.read(cx).snapshot(cx);
10091 let selection = self.selections.newest::<usize>(cx);
10092
10093 // If there is an active Diagnostic Popover jump to its diagnostic instead.
10094 if direction == Direction::Next {
10095 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
10096 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
10097 return;
10098 };
10099 self.activate_diagnostics(
10100 buffer_id,
10101 popover.local_diagnostic.diagnostic.group_id,
10102 window,
10103 cx,
10104 );
10105 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
10106 let primary_range_start = active_diagnostics.primary_range.start;
10107 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10108 let mut new_selection = s.newest_anchor().clone();
10109 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
10110 s.select_anchors(vec![new_selection.clone()]);
10111 });
10112 self.refresh_inline_completion(false, true, window, cx);
10113 }
10114 return;
10115 }
10116 }
10117
10118 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
10119 active_diagnostics
10120 .primary_range
10121 .to_offset(&buffer)
10122 .to_inclusive()
10123 });
10124 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
10125 if active_primary_range.contains(&selection.head()) {
10126 *active_primary_range.start()
10127 } else {
10128 selection.head()
10129 }
10130 } else {
10131 selection.head()
10132 };
10133 let snapshot = self.snapshot(window, cx);
10134 loop {
10135 let mut diagnostics;
10136 if direction == Direction::Prev {
10137 diagnostics = buffer
10138 .diagnostics_in_range::<_, usize>(0..search_start)
10139 .collect::<Vec<_>>();
10140 diagnostics.reverse();
10141 } else {
10142 diagnostics = buffer
10143 .diagnostics_in_range::<_, usize>(search_start..buffer.len())
10144 .collect::<Vec<_>>();
10145 };
10146 let group = diagnostics
10147 .into_iter()
10148 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
10149 // relies on diagnostics_in_range to return diagnostics with the same starting range to
10150 // be sorted in a stable way
10151 // skip until we are at current active diagnostic, if it exists
10152 .skip_while(|entry| {
10153 let is_in_range = match direction {
10154 Direction::Prev => entry.range.end > search_start,
10155 Direction::Next => entry.range.start < search_start,
10156 };
10157 is_in_range
10158 && self
10159 .active_diagnostics
10160 .as_ref()
10161 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
10162 })
10163 .find_map(|entry| {
10164 if entry.diagnostic.is_primary
10165 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
10166 && entry.range.start != entry.range.end
10167 // if we match with the active diagnostic, skip it
10168 && Some(entry.diagnostic.group_id)
10169 != self.active_diagnostics.as_ref().map(|d| d.group_id)
10170 {
10171 Some((entry.range, entry.diagnostic.group_id))
10172 } else {
10173 None
10174 }
10175 });
10176
10177 if let Some((primary_range, group_id)) = group {
10178 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
10179 return;
10180 };
10181 self.activate_diagnostics(buffer_id, group_id, window, cx);
10182 if self.active_diagnostics.is_some() {
10183 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10184 s.select(vec![Selection {
10185 id: selection.id,
10186 start: primary_range.start,
10187 end: primary_range.start,
10188 reversed: false,
10189 goal: SelectionGoal::None,
10190 }]);
10191 });
10192 self.refresh_inline_completion(false, true, window, cx);
10193 }
10194 break;
10195 } else {
10196 // Cycle around to the start of the buffer, potentially moving back to the start of
10197 // the currently active diagnostic.
10198 active_primary_range.take();
10199 if direction == Direction::Prev {
10200 if search_start == buffer.len() {
10201 break;
10202 } else {
10203 search_start = buffer.len();
10204 }
10205 } else if search_start == 0 {
10206 break;
10207 } else {
10208 search_start = 0;
10209 }
10210 }
10211 }
10212 }
10213
10214 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
10215 let snapshot = self.snapshot(window, cx);
10216 let selection = self.selections.newest::<Point>(cx);
10217 self.go_to_hunk_after_position(&snapshot, selection.head(), window, cx);
10218 }
10219
10220 fn go_to_hunk_after_position(
10221 &mut self,
10222 snapshot: &EditorSnapshot,
10223 position: Point,
10224 window: &mut Window,
10225 cx: &mut Context<Editor>,
10226 ) -> Option<MultiBufferDiffHunk> {
10227 let mut hunk = snapshot
10228 .buffer_snapshot
10229 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
10230 .find(|hunk| hunk.row_range.start.0 > position.row);
10231 if hunk.is_none() {
10232 hunk = snapshot
10233 .buffer_snapshot
10234 .diff_hunks_in_range(Point::zero()..position)
10235 .find(|hunk| hunk.row_range.end.0 < position.row)
10236 }
10237 if let Some(hunk) = &hunk {
10238 let destination = Point::new(hunk.row_range.start.0, 0);
10239 self.unfold_ranges(&[destination..destination], false, false, cx);
10240 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10241 s.select_ranges(vec![destination..destination]);
10242 });
10243 }
10244
10245 hunk
10246 }
10247
10248 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, window: &mut Window, cx: &mut Context<Self>) {
10249 let snapshot = self.snapshot(window, cx);
10250 let selection = self.selections.newest::<Point>(cx);
10251 self.go_to_hunk_before_position(&snapshot, selection.head(), window, cx);
10252 }
10253
10254 fn go_to_hunk_before_position(
10255 &mut self,
10256 snapshot: &EditorSnapshot,
10257 position: Point,
10258 window: &mut Window,
10259 cx: &mut Context<Editor>,
10260 ) -> Option<MultiBufferDiffHunk> {
10261 let mut hunk = snapshot.buffer_snapshot.diff_hunk_before(position);
10262 if hunk.is_none() {
10263 hunk = snapshot.buffer_snapshot.diff_hunk_before(Point::MAX);
10264 }
10265 if let Some(hunk) = &hunk {
10266 let destination = Point::new(hunk.row_range.start.0, 0);
10267 self.unfold_ranges(&[destination..destination], false, false, cx);
10268 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10269 s.select_ranges(vec![destination..destination]);
10270 });
10271 }
10272
10273 hunk
10274 }
10275
10276 pub fn go_to_definition(
10277 &mut self,
10278 _: &GoToDefinition,
10279 window: &mut Window,
10280 cx: &mut Context<Self>,
10281 ) -> Task<Result<Navigated>> {
10282 let definition =
10283 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
10284 cx.spawn_in(window, |editor, mut cx| async move {
10285 if definition.await? == Navigated::Yes {
10286 return Ok(Navigated::Yes);
10287 }
10288 match editor.update_in(&mut cx, |editor, window, cx| {
10289 editor.find_all_references(&FindAllReferences, window, cx)
10290 })? {
10291 Some(references) => references.await,
10292 None => Ok(Navigated::No),
10293 }
10294 })
10295 }
10296
10297 pub fn go_to_declaration(
10298 &mut self,
10299 _: &GoToDeclaration,
10300 window: &mut Window,
10301 cx: &mut Context<Self>,
10302 ) -> Task<Result<Navigated>> {
10303 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
10304 }
10305
10306 pub fn go_to_declaration_split(
10307 &mut self,
10308 _: &GoToDeclaration,
10309 window: &mut Window,
10310 cx: &mut Context<Self>,
10311 ) -> Task<Result<Navigated>> {
10312 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
10313 }
10314
10315 pub fn go_to_implementation(
10316 &mut self,
10317 _: &GoToImplementation,
10318 window: &mut Window,
10319 cx: &mut Context<Self>,
10320 ) -> Task<Result<Navigated>> {
10321 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
10322 }
10323
10324 pub fn go_to_implementation_split(
10325 &mut self,
10326 _: &GoToImplementationSplit,
10327 window: &mut Window,
10328 cx: &mut Context<Self>,
10329 ) -> Task<Result<Navigated>> {
10330 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
10331 }
10332
10333 pub fn go_to_type_definition(
10334 &mut self,
10335 _: &GoToTypeDefinition,
10336 window: &mut Window,
10337 cx: &mut Context<Self>,
10338 ) -> Task<Result<Navigated>> {
10339 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
10340 }
10341
10342 pub fn go_to_definition_split(
10343 &mut self,
10344 _: &GoToDefinitionSplit,
10345 window: &mut Window,
10346 cx: &mut Context<Self>,
10347 ) -> Task<Result<Navigated>> {
10348 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
10349 }
10350
10351 pub fn go_to_type_definition_split(
10352 &mut self,
10353 _: &GoToTypeDefinitionSplit,
10354 window: &mut Window,
10355 cx: &mut Context<Self>,
10356 ) -> Task<Result<Navigated>> {
10357 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
10358 }
10359
10360 fn go_to_definition_of_kind(
10361 &mut self,
10362 kind: GotoDefinitionKind,
10363 split: bool,
10364 window: &mut Window,
10365 cx: &mut Context<Self>,
10366 ) -> Task<Result<Navigated>> {
10367 let Some(provider) = self.semantics_provider.clone() else {
10368 return Task::ready(Ok(Navigated::No));
10369 };
10370 let head = self.selections.newest::<usize>(cx).head();
10371 let buffer = self.buffer.read(cx);
10372 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
10373 text_anchor
10374 } else {
10375 return Task::ready(Ok(Navigated::No));
10376 };
10377
10378 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
10379 return Task::ready(Ok(Navigated::No));
10380 };
10381
10382 cx.spawn_in(window, |editor, mut cx| async move {
10383 let definitions = definitions.await?;
10384 let navigated = editor
10385 .update_in(&mut cx, |editor, window, cx| {
10386 editor.navigate_to_hover_links(
10387 Some(kind),
10388 definitions
10389 .into_iter()
10390 .filter(|location| {
10391 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
10392 })
10393 .map(HoverLink::Text)
10394 .collect::<Vec<_>>(),
10395 split,
10396 window,
10397 cx,
10398 )
10399 })?
10400 .await?;
10401 anyhow::Ok(navigated)
10402 })
10403 }
10404
10405 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
10406 let selection = self.selections.newest_anchor();
10407 let head = selection.head();
10408 let tail = selection.tail();
10409
10410 let Some((buffer, start_position)) =
10411 self.buffer.read(cx).text_anchor_for_position(head, cx)
10412 else {
10413 return;
10414 };
10415
10416 let end_position = if head != tail {
10417 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
10418 return;
10419 };
10420 Some(pos)
10421 } else {
10422 None
10423 };
10424
10425 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
10426 let url = if let Some(end_pos) = end_position {
10427 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
10428 } else {
10429 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
10430 };
10431
10432 if let Some(url) = url {
10433 editor.update(&mut cx, |_, cx| {
10434 cx.open_url(&url);
10435 })
10436 } else {
10437 Ok(())
10438 }
10439 });
10440
10441 url_finder.detach();
10442 }
10443
10444 pub fn open_selected_filename(
10445 &mut self,
10446 _: &OpenSelectedFilename,
10447 window: &mut Window,
10448 cx: &mut Context<Self>,
10449 ) {
10450 let Some(workspace) = self.workspace() else {
10451 return;
10452 };
10453
10454 let position = self.selections.newest_anchor().head();
10455
10456 let Some((buffer, buffer_position)) =
10457 self.buffer.read(cx).text_anchor_for_position(position, cx)
10458 else {
10459 return;
10460 };
10461
10462 let project = self.project.clone();
10463
10464 cx.spawn_in(window, |_, mut cx| async move {
10465 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
10466
10467 if let Some((_, path)) = result {
10468 workspace
10469 .update_in(&mut cx, |workspace, window, cx| {
10470 workspace.open_resolved_path(path, window, cx)
10471 })?
10472 .await?;
10473 }
10474 anyhow::Ok(())
10475 })
10476 .detach();
10477 }
10478
10479 pub(crate) fn navigate_to_hover_links(
10480 &mut self,
10481 kind: Option<GotoDefinitionKind>,
10482 mut definitions: Vec<HoverLink>,
10483 split: bool,
10484 window: &mut Window,
10485 cx: &mut Context<Editor>,
10486 ) -> Task<Result<Navigated>> {
10487 // If there is one definition, just open it directly
10488 if definitions.len() == 1 {
10489 let definition = definitions.pop().unwrap();
10490
10491 enum TargetTaskResult {
10492 Location(Option<Location>),
10493 AlreadyNavigated,
10494 }
10495
10496 let target_task = match definition {
10497 HoverLink::Text(link) => {
10498 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
10499 }
10500 HoverLink::InlayHint(lsp_location, server_id) => {
10501 let computation =
10502 self.compute_target_location(lsp_location, server_id, window, cx);
10503 cx.background_executor().spawn(async move {
10504 let location = computation.await?;
10505 Ok(TargetTaskResult::Location(location))
10506 })
10507 }
10508 HoverLink::Url(url) => {
10509 cx.open_url(&url);
10510 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
10511 }
10512 HoverLink::File(path) => {
10513 if let Some(workspace) = self.workspace() {
10514 cx.spawn_in(window, |_, mut cx| async move {
10515 workspace
10516 .update_in(&mut cx, |workspace, window, cx| {
10517 workspace.open_resolved_path(path, window, cx)
10518 })?
10519 .await
10520 .map(|_| TargetTaskResult::AlreadyNavigated)
10521 })
10522 } else {
10523 Task::ready(Ok(TargetTaskResult::Location(None)))
10524 }
10525 }
10526 };
10527 cx.spawn_in(window, |editor, mut cx| async move {
10528 let target = match target_task.await.context("target resolution task")? {
10529 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
10530 TargetTaskResult::Location(None) => return Ok(Navigated::No),
10531 TargetTaskResult::Location(Some(target)) => target,
10532 };
10533
10534 editor.update_in(&mut cx, |editor, window, cx| {
10535 let Some(workspace) = editor.workspace() else {
10536 return Navigated::No;
10537 };
10538 let pane = workspace.read(cx).active_pane().clone();
10539
10540 let range = target.range.to_point(target.buffer.read(cx));
10541 let range = editor.range_for_match(&range);
10542 let range = collapse_multiline_range(range);
10543
10544 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
10545 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
10546 } else {
10547 window.defer(cx, move |window, cx| {
10548 let target_editor: Entity<Self> =
10549 workspace.update(cx, |workspace, cx| {
10550 let pane = if split {
10551 workspace.adjacent_pane(window, cx)
10552 } else {
10553 workspace.active_pane().clone()
10554 };
10555
10556 workspace.open_project_item(
10557 pane,
10558 target.buffer.clone(),
10559 true,
10560 true,
10561 window,
10562 cx,
10563 )
10564 });
10565 target_editor.update(cx, |target_editor, cx| {
10566 // When selecting a definition in a different buffer, disable the nav history
10567 // to avoid creating a history entry at the previous cursor location.
10568 pane.update(cx, |pane, _| pane.disable_history());
10569 target_editor.go_to_singleton_buffer_range(range, window, cx);
10570 pane.update(cx, |pane, _| pane.enable_history());
10571 });
10572 });
10573 }
10574 Navigated::Yes
10575 })
10576 })
10577 } else if !definitions.is_empty() {
10578 cx.spawn_in(window, |editor, mut cx| async move {
10579 let (title, location_tasks, workspace) = editor
10580 .update_in(&mut cx, |editor, window, cx| {
10581 let tab_kind = match kind {
10582 Some(GotoDefinitionKind::Implementation) => "Implementations",
10583 _ => "Definitions",
10584 };
10585 let title = definitions
10586 .iter()
10587 .find_map(|definition| match definition {
10588 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
10589 let buffer = origin.buffer.read(cx);
10590 format!(
10591 "{} for {}",
10592 tab_kind,
10593 buffer
10594 .text_for_range(origin.range.clone())
10595 .collect::<String>()
10596 )
10597 }),
10598 HoverLink::InlayHint(_, _) => None,
10599 HoverLink::Url(_) => None,
10600 HoverLink::File(_) => None,
10601 })
10602 .unwrap_or(tab_kind.to_string());
10603 let location_tasks = definitions
10604 .into_iter()
10605 .map(|definition| match definition {
10606 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
10607 HoverLink::InlayHint(lsp_location, server_id) => editor
10608 .compute_target_location(lsp_location, server_id, window, cx),
10609 HoverLink::Url(_) => Task::ready(Ok(None)),
10610 HoverLink::File(_) => Task::ready(Ok(None)),
10611 })
10612 .collect::<Vec<_>>();
10613 (title, location_tasks, editor.workspace().clone())
10614 })
10615 .context("location tasks preparation")?;
10616
10617 let locations = future::join_all(location_tasks)
10618 .await
10619 .into_iter()
10620 .filter_map(|location| location.transpose())
10621 .collect::<Result<_>>()
10622 .context("location tasks")?;
10623
10624 let Some(workspace) = workspace else {
10625 return Ok(Navigated::No);
10626 };
10627 let opened = workspace
10628 .update_in(&mut cx, |workspace, window, cx| {
10629 Self::open_locations_in_multibuffer(
10630 workspace,
10631 locations,
10632 title,
10633 split,
10634 MultibufferSelectionMode::First,
10635 window,
10636 cx,
10637 )
10638 })
10639 .ok();
10640
10641 anyhow::Ok(Navigated::from_bool(opened.is_some()))
10642 })
10643 } else {
10644 Task::ready(Ok(Navigated::No))
10645 }
10646 }
10647
10648 fn compute_target_location(
10649 &self,
10650 lsp_location: lsp::Location,
10651 server_id: LanguageServerId,
10652 window: &mut Window,
10653 cx: &mut Context<Self>,
10654 ) -> Task<anyhow::Result<Option<Location>>> {
10655 let Some(project) = self.project.clone() else {
10656 return Task::ready(Ok(None));
10657 };
10658
10659 cx.spawn_in(window, move |editor, mut cx| async move {
10660 let location_task = editor.update(&mut cx, |_, cx| {
10661 project.update(cx, |project, cx| {
10662 let language_server_name = project
10663 .language_server_statuses(cx)
10664 .find(|(id, _)| server_id == *id)
10665 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
10666 language_server_name.map(|language_server_name| {
10667 project.open_local_buffer_via_lsp(
10668 lsp_location.uri.clone(),
10669 server_id,
10670 language_server_name,
10671 cx,
10672 )
10673 })
10674 })
10675 })?;
10676 let location = match location_task {
10677 Some(task) => Some({
10678 let target_buffer_handle = task.await.context("open local buffer")?;
10679 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
10680 let target_start = target_buffer
10681 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
10682 let target_end = target_buffer
10683 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
10684 target_buffer.anchor_after(target_start)
10685 ..target_buffer.anchor_before(target_end)
10686 })?;
10687 Location {
10688 buffer: target_buffer_handle,
10689 range,
10690 }
10691 }),
10692 None => None,
10693 };
10694 Ok(location)
10695 })
10696 }
10697
10698 pub fn find_all_references(
10699 &mut self,
10700 _: &FindAllReferences,
10701 window: &mut Window,
10702 cx: &mut Context<Self>,
10703 ) -> Option<Task<Result<Navigated>>> {
10704 let selection = self.selections.newest::<usize>(cx);
10705 let multi_buffer = self.buffer.read(cx);
10706 let head = selection.head();
10707
10708 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
10709 let head_anchor = multi_buffer_snapshot.anchor_at(
10710 head,
10711 if head < selection.tail() {
10712 Bias::Right
10713 } else {
10714 Bias::Left
10715 },
10716 );
10717
10718 match self
10719 .find_all_references_task_sources
10720 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
10721 {
10722 Ok(_) => {
10723 log::info!(
10724 "Ignoring repeated FindAllReferences invocation with the position of already running task"
10725 );
10726 return None;
10727 }
10728 Err(i) => {
10729 self.find_all_references_task_sources.insert(i, head_anchor);
10730 }
10731 }
10732
10733 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
10734 let workspace = self.workspace()?;
10735 let project = workspace.read(cx).project().clone();
10736 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
10737 Some(cx.spawn_in(window, |editor, mut cx| async move {
10738 let _cleanup = defer({
10739 let mut cx = cx.clone();
10740 move || {
10741 let _ = editor.update(&mut cx, |editor, _| {
10742 if let Ok(i) =
10743 editor
10744 .find_all_references_task_sources
10745 .binary_search_by(|anchor| {
10746 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
10747 })
10748 {
10749 editor.find_all_references_task_sources.remove(i);
10750 }
10751 });
10752 }
10753 });
10754
10755 let locations = references.await?;
10756 if locations.is_empty() {
10757 return anyhow::Ok(Navigated::No);
10758 }
10759
10760 workspace.update_in(&mut cx, |workspace, window, cx| {
10761 let title = locations
10762 .first()
10763 .as_ref()
10764 .map(|location| {
10765 let buffer = location.buffer.read(cx);
10766 format!(
10767 "References to `{}`",
10768 buffer
10769 .text_for_range(location.range.clone())
10770 .collect::<String>()
10771 )
10772 })
10773 .unwrap();
10774 Self::open_locations_in_multibuffer(
10775 workspace,
10776 locations,
10777 title,
10778 false,
10779 MultibufferSelectionMode::First,
10780 window,
10781 cx,
10782 );
10783 Navigated::Yes
10784 })
10785 }))
10786 }
10787
10788 /// Opens a multibuffer with the given project locations in it
10789 pub fn open_locations_in_multibuffer(
10790 workspace: &mut Workspace,
10791 mut locations: Vec<Location>,
10792 title: String,
10793 split: bool,
10794 multibuffer_selection_mode: MultibufferSelectionMode,
10795 window: &mut Window,
10796 cx: &mut Context<Workspace>,
10797 ) {
10798 // If there are multiple definitions, open them in a multibuffer
10799 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
10800 let mut locations = locations.into_iter().peekable();
10801 let mut ranges = Vec::new();
10802 let capability = workspace.project().read(cx).capability();
10803
10804 let excerpt_buffer = cx.new(|cx| {
10805 let mut multibuffer = MultiBuffer::new(capability);
10806 while let Some(location) = locations.next() {
10807 let buffer = location.buffer.read(cx);
10808 let mut ranges_for_buffer = Vec::new();
10809 let range = location.range.to_offset(buffer);
10810 ranges_for_buffer.push(range.clone());
10811
10812 while let Some(next_location) = locations.peek() {
10813 if next_location.buffer == location.buffer {
10814 ranges_for_buffer.push(next_location.range.to_offset(buffer));
10815 locations.next();
10816 } else {
10817 break;
10818 }
10819 }
10820
10821 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
10822 ranges.extend(multibuffer.push_excerpts_with_context_lines(
10823 location.buffer.clone(),
10824 ranges_for_buffer,
10825 DEFAULT_MULTIBUFFER_CONTEXT,
10826 cx,
10827 ))
10828 }
10829
10830 multibuffer.with_title(title)
10831 });
10832
10833 let editor = cx.new(|cx| {
10834 Editor::for_multibuffer(
10835 excerpt_buffer,
10836 Some(workspace.project().clone()),
10837 true,
10838 window,
10839 cx,
10840 )
10841 });
10842 editor.update(cx, |editor, cx| {
10843 match multibuffer_selection_mode {
10844 MultibufferSelectionMode::First => {
10845 if let Some(first_range) = ranges.first() {
10846 editor.change_selections(None, window, cx, |selections| {
10847 selections.clear_disjoint();
10848 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
10849 });
10850 }
10851 editor.highlight_background::<Self>(
10852 &ranges,
10853 |theme| theme.editor_highlighted_line_background,
10854 cx,
10855 );
10856 }
10857 MultibufferSelectionMode::All => {
10858 editor.change_selections(None, window, cx, |selections| {
10859 selections.clear_disjoint();
10860 selections.select_anchor_ranges(ranges);
10861 });
10862 }
10863 }
10864 editor.register_buffers_with_language_servers(cx);
10865 });
10866
10867 let item = Box::new(editor);
10868 let item_id = item.item_id();
10869
10870 if split {
10871 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
10872 } else {
10873 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10874 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10875 pane.close_current_preview_item(window, cx)
10876 } else {
10877 None
10878 }
10879 });
10880 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
10881 }
10882 workspace.active_pane().update(cx, |pane, cx| {
10883 pane.set_preview_item_id(Some(item_id), cx);
10884 });
10885 }
10886
10887 pub fn rename(
10888 &mut self,
10889 _: &Rename,
10890 window: &mut Window,
10891 cx: &mut Context<Self>,
10892 ) -> Option<Task<Result<()>>> {
10893 use language::ToOffset as _;
10894
10895 let provider = self.semantics_provider.clone()?;
10896 let selection = self.selections.newest_anchor().clone();
10897 let (cursor_buffer, cursor_buffer_position) = self
10898 .buffer
10899 .read(cx)
10900 .text_anchor_for_position(selection.head(), cx)?;
10901 let (tail_buffer, cursor_buffer_position_end) = self
10902 .buffer
10903 .read(cx)
10904 .text_anchor_for_position(selection.tail(), cx)?;
10905 if tail_buffer != cursor_buffer {
10906 return None;
10907 }
10908
10909 let snapshot = cursor_buffer.read(cx).snapshot();
10910 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10911 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10912 let prepare_rename = provider
10913 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10914 .unwrap_or_else(|| Task::ready(Ok(None)));
10915 drop(snapshot);
10916
10917 Some(cx.spawn_in(window, |this, mut cx| async move {
10918 let rename_range = if let Some(range) = prepare_rename.await? {
10919 Some(range)
10920 } else {
10921 this.update(&mut cx, |this, cx| {
10922 let buffer = this.buffer.read(cx).snapshot(cx);
10923 let mut buffer_highlights = this
10924 .document_highlights_for_position(selection.head(), &buffer)
10925 .filter(|highlight| {
10926 highlight.start.excerpt_id == selection.head().excerpt_id
10927 && highlight.end.excerpt_id == selection.head().excerpt_id
10928 });
10929 buffer_highlights
10930 .next()
10931 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10932 })?
10933 };
10934 if let Some(rename_range) = rename_range {
10935 this.update_in(&mut cx, |this, window, cx| {
10936 let snapshot = cursor_buffer.read(cx).snapshot();
10937 let rename_buffer_range = rename_range.to_offset(&snapshot);
10938 let cursor_offset_in_rename_range =
10939 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10940 let cursor_offset_in_rename_range_end =
10941 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10942
10943 this.take_rename(false, window, cx);
10944 let buffer = this.buffer.read(cx).read(cx);
10945 let cursor_offset = selection.head().to_offset(&buffer);
10946 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10947 let rename_end = rename_start + rename_buffer_range.len();
10948 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10949 let mut old_highlight_id = None;
10950 let old_name: Arc<str> = buffer
10951 .chunks(rename_start..rename_end, true)
10952 .map(|chunk| {
10953 if old_highlight_id.is_none() {
10954 old_highlight_id = chunk.syntax_highlight_id;
10955 }
10956 chunk.text
10957 })
10958 .collect::<String>()
10959 .into();
10960
10961 drop(buffer);
10962
10963 // Position the selection in the rename editor so that it matches the current selection.
10964 this.show_local_selections = false;
10965 let rename_editor = cx.new(|cx| {
10966 let mut editor = Editor::single_line(window, cx);
10967 editor.buffer.update(cx, |buffer, cx| {
10968 buffer.edit([(0..0, old_name.clone())], None, cx)
10969 });
10970 let rename_selection_range = match cursor_offset_in_rename_range
10971 .cmp(&cursor_offset_in_rename_range_end)
10972 {
10973 Ordering::Equal => {
10974 editor.select_all(&SelectAll, window, cx);
10975 return editor;
10976 }
10977 Ordering::Less => {
10978 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10979 }
10980 Ordering::Greater => {
10981 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10982 }
10983 };
10984 if rename_selection_range.end > old_name.len() {
10985 editor.select_all(&SelectAll, window, cx);
10986 } else {
10987 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10988 s.select_ranges([rename_selection_range]);
10989 });
10990 }
10991 editor
10992 });
10993 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10994 if e == &EditorEvent::Focused {
10995 cx.emit(EditorEvent::FocusedIn)
10996 }
10997 })
10998 .detach();
10999
11000 let write_highlights =
11001 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
11002 let read_highlights =
11003 this.clear_background_highlights::<DocumentHighlightRead>(cx);
11004 let ranges = write_highlights
11005 .iter()
11006 .flat_map(|(_, ranges)| ranges.iter())
11007 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
11008 .cloned()
11009 .collect();
11010
11011 this.highlight_text::<Rename>(
11012 ranges,
11013 HighlightStyle {
11014 fade_out: Some(0.6),
11015 ..Default::default()
11016 },
11017 cx,
11018 );
11019 let rename_focus_handle = rename_editor.focus_handle(cx);
11020 window.focus(&rename_focus_handle);
11021 let block_id = this.insert_blocks(
11022 [BlockProperties {
11023 style: BlockStyle::Flex,
11024 placement: BlockPlacement::Below(range.start),
11025 height: 1,
11026 render: Arc::new({
11027 let rename_editor = rename_editor.clone();
11028 move |cx: &mut BlockContext| {
11029 let mut text_style = cx.editor_style.text.clone();
11030 if let Some(highlight_style) = old_highlight_id
11031 .and_then(|h| h.style(&cx.editor_style.syntax))
11032 {
11033 text_style = text_style.highlight(highlight_style);
11034 }
11035 div()
11036 .block_mouse_down()
11037 .pl(cx.anchor_x)
11038 .child(EditorElement::new(
11039 &rename_editor,
11040 EditorStyle {
11041 background: cx.theme().system().transparent,
11042 local_player: cx.editor_style.local_player,
11043 text: text_style,
11044 scrollbar_width: cx.editor_style.scrollbar_width,
11045 syntax: cx.editor_style.syntax.clone(),
11046 status: cx.editor_style.status.clone(),
11047 inlay_hints_style: HighlightStyle {
11048 font_weight: Some(FontWeight::BOLD),
11049 ..make_inlay_hints_style(cx.app)
11050 },
11051 inline_completion_styles: make_suggestion_styles(
11052 cx.app,
11053 ),
11054 ..EditorStyle::default()
11055 },
11056 ))
11057 .into_any_element()
11058 }
11059 }),
11060 priority: 0,
11061 }],
11062 Some(Autoscroll::fit()),
11063 cx,
11064 )[0];
11065 this.pending_rename = Some(RenameState {
11066 range,
11067 old_name,
11068 editor: rename_editor,
11069 block_id,
11070 });
11071 })?;
11072 }
11073
11074 Ok(())
11075 }))
11076 }
11077
11078 pub fn confirm_rename(
11079 &mut self,
11080 _: &ConfirmRename,
11081 window: &mut Window,
11082 cx: &mut Context<Self>,
11083 ) -> Option<Task<Result<()>>> {
11084 let rename = self.take_rename(false, window, cx)?;
11085 let workspace = self.workspace()?.downgrade();
11086 let (buffer, start) = self
11087 .buffer
11088 .read(cx)
11089 .text_anchor_for_position(rename.range.start, cx)?;
11090 let (end_buffer, _) = self
11091 .buffer
11092 .read(cx)
11093 .text_anchor_for_position(rename.range.end, cx)?;
11094 if buffer != end_buffer {
11095 return None;
11096 }
11097
11098 let old_name = rename.old_name;
11099 let new_name = rename.editor.read(cx).text(cx);
11100
11101 let rename = self.semantics_provider.as_ref()?.perform_rename(
11102 &buffer,
11103 start,
11104 new_name.clone(),
11105 cx,
11106 )?;
11107
11108 Some(cx.spawn_in(window, |editor, mut cx| async move {
11109 let project_transaction = rename.await?;
11110 Self::open_project_transaction(
11111 &editor,
11112 workspace,
11113 project_transaction,
11114 format!("Rename: {} → {}", old_name, new_name),
11115 cx.clone(),
11116 )
11117 .await?;
11118
11119 editor.update(&mut cx, |editor, cx| {
11120 editor.refresh_document_highlights(cx);
11121 })?;
11122 Ok(())
11123 }))
11124 }
11125
11126 fn take_rename(
11127 &mut self,
11128 moving_cursor: bool,
11129 window: &mut Window,
11130 cx: &mut Context<Self>,
11131 ) -> Option<RenameState> {
11132 let rename = self.pending_rename.take()?;
11133 if rename.editor.focus_handle(cx).is_focused(window) {
11134 window.focus(&self.focus_handle);
11135 }
11136
11137 self.remove_blocks(
11138 [rename.block_id].into_iter().collect(),
11139 Some(Autoscroll::fit()),
11140 cx,
11141 );
11142 self.clear_highlights::<Rename>(cx);
11143 self.show_local_selections = true;
11144
11145 if moving_cursor {
11146 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
11147 editor.selections.newest::<usize>(cx).head()
11148 });
11149
11150 // Update the selection to match the position of the selection inside
11151 // the rename editor.
11152 let snapshot = self.buffer.read(cx).read(cx);
11153 let rename_range = rename.range.to_offset(&snapshot);
11154 let cursor_in_editor = snapshot
11155 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
11156 .min(rename_range.end);
11157 drop(snapshot);
11158
11159 self.change_selections(None, window, cx, |s| {
11160 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
11161 });
11162 } else {
11163 self.refresh_document_highlights(cx);
11164 }
11165
11166 Some(rename)
11167 }
11168
11169 pub fn pending_rename(&self) -> Option<&RenameState> {
11170 self.pending_rename.as_ref()
11171 }
11172
11173 fn format(
11174 &mut self,
11175 _: &Format,
11176 window: &mut Window,
11177 cx: &mut Context<Self>,
11178 ) -> Option<Task<Result<()>>> {
11179 let project = match &self.project {
11180 Some(project) => project.clone(),
11181 None => return None,
11182 };
11183
11184 Some(self.perform_format(
11185 project,
11186 FormatTrigger::Manual,
11187 FormatTarget::Buffers,
11188 window,
11189 cx,
11190 ))
11191 }
11192
11193 fn format_selections(
11194 &mut self,
11195 _: &FormatSelections,
11196 window: &mut Window,
11197 cx: &mut Context<Self>,
11198 ) -> Option<Task<Result<()>>> {
11199 let project = match &self.project {
11200 Some(project) => project.clone(),
11201 None => return None,
11202 };
11203
11204 let ranges = self
11205 .selections
11206 .all_adjusted(cx)
11207 .into_iter()
11208 .map(|selection| selection.range())
11209 .collect_vec();
11210
11211 Some(self.perform_format(
11212 project,
11213 FormatTrigger::Manual,
11214 FormatTarget::Ranges(ranges),
11215 window,
11216 cx,
11217 ))
11218 }
11219
11220 fn perform_format(
11221 &mut self,
11222 project: Entity<Project>,
11223 trigger: FormatTrigger,
11224 target: FormatTarget,
11225 window: &mut Window,
11226 cx: &mut Context<Self>,
11227 ) -> Task<Result<()>> {
11228 let buffer = self.buffer.clone();
11229 let (buffers, target) = match target {
11230 FormatTarget::Buffers => {
11231 let mut buffers = buffer.read(cx).all_buffers();
11232 if trigger == FormatTrigger::Save {
11233 buffers.retain(|buffer| buffer.read(cx).is_dirty());
11234 }
11235 (buffers, LspFormatTarget::Buffers)
11236 }
11237 FormatTarget::Ranges(selection_ranges) => {
11238 let multi_buffer = buffer.read(cx);
11239 let snapshot = multi_buffer.read(cx);
11240 let mut buffers = HashSet::default();
11241 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
11242 BTreeMap::new();
11243 for selection_range in selection_ranges {
11244 for (buffer, buffer_range, _) in
11245 snapshot.range_to_buffer_ranges(selection_range)
11246 {
11247 let buffer_id = buffer.remote_id();
11248 let start = buffer.anchor_before(buffer_range.start);
11249 let end = buffer.anchor_after(buffer_range.end);
11250 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
11251 buffer_id_to_ranges
11252 .entry(buffer_id)
11253 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
11254 .or_insert_with(|| vec![start..end]);
11255 }
11256 }
11257 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
11258 }
11259 };
11260
11261 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
11262 let format = project.update(cx, |project, cx| {
11263 project.format(buffers, target, true, trigger, cx)
11264 });
11265
11266 cx.spawn_in(window, |_, mut cx| async move {
11267 let transaction = futures::select_biased! {
11268 () = timeout => {
11269 log::warn!("timed out waiting for formatting");
11270 None
11271 }
11272 transaction = format.log_err().fuse() => transaction,
11273 };
11274
11275 buffer
11276 .update(&mut cx, |buffer, cx| {
11277 if let Some(transaction) = transaction {
11278 if !buffer.is_singleton() {
11279 buffer.push_transaction(&transaction.0, cx);
11280 }
11281 }
11282
11283 cx.notify();
11284 })
11285 .ok();
11286
11287 Ok(())
11288 })
11289 }
11290
11291 fn restart_language_server(
11292 &mut self,
11293 _: &RestartLanguageServer,
11294 _: &mut Window,
11295 cx: &mut Context<Self>,
11296 ) {
11297 if let Some(project) = self.project.clone() {
11298 self.buffer.update(cx, |multi_buffer, cx| {
11299 project.update(cx, |project, cx| {
11300 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
11301 });
11302 })
11303 }
11304 }
11305
11306 fn cancel_language_server_work(
11307 &mut self,
11308 _: &actions::CancelLanguageServerWork,
11309 _: &mut Window,
11310 cx: &mut Context<Self>,
11311 ) {
11312 if let Some(project) = self.project.clone() {
11313 self.buffer.update(cx, |multi_buffer, cx| {
11314 project.update(cx, |project, cx| {
11315 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
11316 });
11317 })
11318 }
11319 }
11320
11321 fn show_character_palette(
11322 &mut self,
11323 _: &ShowCharacterPalette,
11324 window: &mut Window,
11325 _: &mut Context<Self>,
11326 ) {
11327 window.show_character_palette();
11328 }
11329
11330 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
11331 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
11332 let buffer = self.buffer.read(cx).snapshot(cx);
11333 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
11334 let is_valid = buffer
11335 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
11336 .any(|entry| {
11337 entry.diagnostic.is_primary
11338 && !entry.range.is_empty()
11339 && entry.range.start == primary_range_start
11340 && entry.diagnostic.message == active_diagnostics.primary_message
11341 });
11342
11343 if is_valid != active_diagnostics.is_valid {
11344 active_diagnostics.is_valid = is_valid;
11345 let mut new_styles = HashMap::default();
11346 for (block_id, diagnostic) in &active_diagnostics.blocks {
11347 new_styles.insert(
11348 *block_id,
11349 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
11350 );
11351 }
11352 self.display_map.update(cx, |display_map, _cx| {
11353 display_map.replace_blocks(new_styles)
11354 });
11355 }
11356 }
11357 }
11358
11359 fn activate_diagnostics(
11360 &mut self,
11361 buffer_id: BufferId,
11362 group_id: usize,
11363 window: &mut Window,
11364 cx: &mut Context<Self>,
11365 ) {
11366 self.dismiss_diagnostics(cx);
11367 let snapshot = self.snapshot(window, cx);
11368 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
11369 let buffer = self.buffer.read(cx).snapshot(cx);
11370
11371 let mut primary_range = None;
11372 let mut primary_message = None;
11373 let diagnostic_group = buffer
11374 .diagnostic_group(buffer_id, group_id)
11375 .filter_map(|entry| {
11376 let start = entry.range.start;
11377 let end = entry.range.end;
11378 if snapshot.is_line_folded(MultiBufferRow(start.row))
11379 && (start.row == end.row
11380 || snapshot.is_line_folded(MultiBufferRow(end.row)))
11381 {
11382 return None;
11383 }
11384 if entry.diagnostic.is_primary {
11385 primary_range = Some(entry.range.clone());
11386 primary_message = Some(entry.diagnostic.message.clone());
11387 }
11388 Some(entry)
11389 })
11390 .collect::<Vec<_>>();
11391 let primary_range = primary_range?;
11392 let primary_message = primary_message?;
11393
11394 let blocks = display_map
11395 .insert_blocks(
11396 diagnostic_group.iter().map(|entry| {
11397 let diagnostic = entry.diagnostic.clone();
11398 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
11399 BlockProperties {
11400 style: BlockStyle::Fixed,
11401 placement: BlockPlacement::Below(
11402 buffer.anchor_after(entry.range.start),
11403 ),
11404 height: message_height,
11405 render: diagnostic_block_renderer(diagnostic, None, true, true),
11406 priority: 0,
11407 }
11408 }),
11409 cx,
11410 )
11411 .into_iter()
11412 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
11413 .collect();
11414
11415 Some(ActiveDiagnosticGroup {
11416 primary_range: buffer.anchor_before(primary_range.start)
11417 ..buffer.anchor_after(primary_range.end),
11418 primary_message,
11419 group_id,
11420 blocks,
11421 is_valid: true,
11422 })
11423 });
11424 }
11425
11426 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
11427 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
11428 self.display_map.update(cx, |display_map, cx| {
11429 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
11430 });
11431 cx.notify();
11432 }
11433 }
11434
11435 pub fn set_selections_from_remote(
11436 &mut self,
11437 selections: Vec<Selection<Anchor>>,
11438 pending_selection: Option<Selection<Anchor>>,
11439 window: &mut Window,
11440 cx: &mut Context<Self>,
11441 ) {
11442 let old_cursor_position = self.selections.newest_anchor().head();
11443 self.selections.change_with(cx, |s| {
11444 s.select_anchors(selections);
11445 if let Some(pending_selection) = pending_selection {
11446 s.set_pending(pending_selection, SelectMode::Character);
11447 } else {
11448 s.clear_pending();
11449 }
11450 });
11451 self.selections_did_change(false, &old_cursor_position, true, window, cx);
11452 }
11453
11454 fn push_to_selection_history(&mut self) {
11455 self.selection_history.push(SelectionHistoryEntry {
11456 selections: self.selections.disjoint_anchors(),
11457 select_next_state: self.select_next_state.clone(),
11458 select_prev_state: self.select_prev_state.clone(),
11459 add_selections_state: self.add_selections_state.clone(),
11460 });
11461 }
11462
11463 pub fn transact(
11464 &mut self,
11465 window: &mut Window,
11466 cx: &mut Context<Self>,
11467 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
11468 ) -> Option<TransactionId> {
11469 self.start_transaction_at(Instant::now(), window, cx);
11470 update(self, window, cx);
11471 self.end_transaction_at(Instant::now(), cx)
11472 }
11473
11474 pub fn start_transaction_at(
11475 &mut self,
11476 now: Instant,
11477 window: &mut Window,
11478 cx: &mut Context<Self>,
11479 ) {
11480 self.end_selection(window, cx);
11481 if let Some(tx_id) = self
11482 .buffer
11483 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
11484 {
11485 self.selection_history
11486 .insert_transaction(tx_id, self.selections.disjoint_anchors());
11487 cx.emit(EditorEvent::TransactionBegun {
11488 transaction_id: tx_id,
11489 })
11490 }
11491 }
11492
11493 pub fn end_transaction_at(
11494 &mut self,
11495 now: Instant,
11496 cx: &mut Context<Self>,
11497 ) -> Option<TransactionId> {
11498 if let Some(transaction_id) = self
11499 .buffer
11500 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
11501 {
11502 if let Some((_, end_selections)) =
11503 self.selection_history.transaction_mut(transaction_id)
11504 {
11505 *end_selections = Some(self.selections.disjoint_anchors());
11506 } else {
11507 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
11508 }
11509
11510 cx.emit(EditorEvent::Edited { transaction_id });
11511 Some(transaction_id)
11512 } else {
11513 None
11514 }
11515 }
11516
11517 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
11518 if self.selection_mark_mode {
11519 self.change_selections(None, window, cx, |s| {
11520 s.move_with(|_, sel| {
11521 sel.collapse_to(sel.head(), SelectionGoal::None);
11522 });
11523 })
11524 }
11525 self.selection_mark_mode = true;
11526 cx.notify();
11527 }
11528
11529 pub fn swap_selection_ends(
11530 &mut self,
11531 _: &actions::SwapSelectionEnds,
11532 window: &mut Window,
11533 cx: &mut Context<Self>,
11534 ) {
11535 self.change_selections(None, window, cx, |s| {
11536 s.move_with(|_, sel| {
11537 if sel.start != sel.end {
11538 sel.reversed = !sel.reversed
11539 }
11540 });
11541 });
11542 self.request_autoscroll(Autoscroll::newest(), cx);
11543 cx.notify();
11544 }
11545
11546 pub fn toggle_fold(
11547 &mut self,
11548 _: &actions::ToggleFold,
11549 window: &mut Window,
11550 cx: &mut Context<Self>,
11551 ) {
11552 if self.is_singleton(cx) {
11553 let selection = self.selections.newest::<Point>(cx);
11554
11555 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11556 let range = if selection.is_empty() {
11557 let point = selection.head().to_display_point(&display_map);
11558 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11559 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11560 .to_point(&display_map);
11561 start..end
11562 } else {
11563 selection.range()
11564 };
11565 if display_map.folds_in_range(range).next().is_some() {
11566 self.unfold_lines(&Default::default(), window, cx)
11567 } else {
11568 self.fold(&Default::default(), window, cx)
11569 }
11570 } else {
11571 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11572 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11573 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11574 .map(|(snapshot, _, _)| snapshot.remote_id())
11575 .collect();
11576
11577 for buffer_id in buffer_ids {
11578 if self.is_buffer_folded(buffer_id, cx) {
11579 self.unfold_buffer(buffer_id, cx);
11580 } else {
11581 self.fold_buffer(buffer_id, cx);
11582 }
11583 }
11584 }
11585 }
11586
11587 pub fn toggle_fold_recursive(
11588 &mut self,
11589 _: &actions::ToggleFoldRecursive,
11590 window: &mut Window,
11591 cx: &mut Context<Self>,
11592 ) {
11593 let selection = self.selections.newest::<Point>(cx);
11594
11595 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11596 let range = if selection.is_empty() {
11597 let point = selection.head().to_display_point(&display_map);
11598 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
11599 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
11600 .to_point(&display_map);
11601 start..end
11602 } else {
11603 selection.range()
11604 };
11605 if display_map.folds_in_range(range).next().is_some() {
11606 self.unfold_recursive(&Default::default(), window, cx)
11607 } else {
11608 self.fold_recursive(&Default::default(), window, cx)
11609 }
11610 }
11611
11612 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
11613 if self.is_singleton(cx) {
11614 let mut to_fold = Vec::new();
11615 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11616 let selections = self.selections.all_adjusted(cx);
11617
11618 for selection in selections {
11619 let range = selection.range().sorted();
11620 let buffer_start_row = range.start.row;
11621
11622 if range.start.row != range.end.row {
11623 let mut found = false;
11624 let mut row = range.start.row;
11625 while row <= range.end.row {
11626 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
11627 {
11628 found = true;
11629 row = crease.range().end.row + 1;
11630 to_fold.push(crease);
11631 } else {
11632 row += 1
11633 }
11634 }
11635 if found {
11636 continue;
11637 }
11638 }
11639
11640 for row in (0..=range.start.row).rev() {
11641 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11642 if crease.range().end.row >= buffer_start_row {
11643 to_fold.push(crease);
11644 if row <= range.start.row {
11645 break;
11646 }
11647 }
11648 }
11649 }
11650 }
11651
11652 self.fold_creases(to_fold, true, window, cx);
11653 } else {
11654 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11655
11656 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11657 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11658 .map(|(snapshot, _, _)| snapshot.remote_id())
11659 .collect();
11660 for buffer_id in buffer_ids {
11661 self.fold_buffer(buffer_id, cx);
11662 }
11663 }
11664 }
11665
11666 fn fold_at_level(
11667 &mut self,
11668 fold_at: &FoldAtLevel,
11669 window: &mut Window,
11670 cx: &mut Context<Self>,
11671 ) {
11672 if !self.buffer.read(cx).is_singleton() {
11673 return;
11674 }
11675
11676 let fold_at_level = fold_at.level;
11677 let snapshot = self.buffer.read(cx).snapshot(cx);
11678 let mut to_fold = Vec::new();
11679 let mut stack = vec![(0, snapshot.max_row().0, 1)];
11680
11681 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
11682 while start_row < end_row {
11683 match self
11684 .snapshot(window, cx)
11685 .crease_for_buffer_row(MultiBufferRow(start_row))
11686 {
11687 Some(crease) => {
11688 let nested_start_row = crease.range().start.row + 1;
11689 let nested_end_row = crease.range().end.row;
11690
11691 if current_level < fold_at_level {
11692 stack.push((nested_start_row, nested_end_row, current_level + 1));
11693 } else if current_level == fold_at_level {
11694 to_fold.push(crease);
11695 }
11696
11697 start_row = nested_end_row + 1;
11698 }
11699 None => start_row += 1,
11700 }
11701 }
11702 }
11703
11704 self.fold_creases(to_fold, true, window, cx);
11705 }
11706
11707 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
11708 if self.buffer.read(cx).is_singleton() {
11709 let mut fold_ranges = Vec::new();
11710 let snapshot = self.buffer.read(cx).snapshot(cx);
11711
11712 for row in 0..snapshot.max_row().0 {
11713 if let Some(foldable_range) = self
11714 .snapshot(window, cx)
11715 .crease_for_buffer_row(MultiBufferRow(row))
11716 {
11717 fold_ranges.push(foldable_range);
11718 }
11719 }
11720
11721 self.fold_creases(fold_ranges, true, window, cx);
11722 } else {
11723 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
11724 editor
11725 .update_in(&mut cx, |editor, _, cx| {
11726 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
11727 editor.fold_buffer(buffer_id, cx);
11728 }
11729 })
11730 .ok();
11731 });
11732 }
11733 }
11734
11735 pub fn fold_function_bodies(
11736 &mut self,
11737 _: &actions::FoldFunctionBodies,
11738 window: &mut Window,
11739 cx: &mut Context<Self>,
11740 ) {
11741 let snapshot = self.buffer.read(cx).snapshot(cx);
11742
11743 let ranges = snapshot
11744 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
11745 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
11746 .collect::<Vec<_>>();
11747
11748 let creases = ranges
11749 .into_iter()
11750 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
11751 .collect();
11752
11753 self.fold_creases(creases, true, window, cx);
11754 }
11755
11756 pub fn fold_recursive(
11757 &mut self,
11758 _: &actions::FoldRecursive,
11759 window: &mut Window,
11760 cx: &mut Context<Self>,
11761 ) {
11762 let mut to_fold = Vec::new();
11763 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11764 let selections = self.selections.all_adjusted(cx);
11765
11766 for selection in selections {
11767 let range = selection.range().sorted();
11768 let buffer_start_row = range.start.row;
11769
11770 if range.start.row != range.end.row {
11771 let mut found = false;
11772 for row in range.start.row..=range.end.row {
11773 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11774 found = true;
11775 to_fold.push(crease);
11776 }
11777 }
11778 if found {
11779 continue;
11780 }
11781 }
11782
11783 for row in (0..=range.start.row).rev() {
11784 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
11785 if crease.range().end.row >= buffer_start_row {
11786 to_fold.push(crease);
11787 } else {
11788 break;
11789 }
11790 }
11791 }
11792 }
11793
11794 self.fold_creases(to_fold, true, window, cx);
11795 }
11796
11797 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
11798 let buffer_row = fold_at.buffer_row;
11799 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11800
11801 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
11802 let autoscroll = self
11803 .selections
11804 .all::<Point>(cx)
11805 .iter()
11806 .any(|selection| crease.range().overlaps(&selection.range()));
11807
11808 self.fold_creases(vec![crease], autoscroll, window, cx);
11809 }
11810 }
11811
11812 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
11813 if self.is_singleton(cx) {
11814 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11815 let buffer = &display_map.buffer_snapshot;
11816 let selections = self.selections.all::<Point>(cx);
11817 let ranges = selections
11818 .iter()
11819 .map(|s| {
11820 let range = s.display_range(&display_map).sorted();
11821 let mut start = range.start.to_point(&display_map);
11822 let mut end = range.end.to_point(&display_map);
11823 start.column = 0;
11824 end.column = buffer.line_len(MultiBufferRow(end.row));
11825 start..end
11826 })
11827 .collect::<Vec<_>>();
11828
11829 self.unfold_ranges(&ranges, true, true, cx);
11830 } else {
11831 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
11832 let buffer_ids: HashSet<_> = multi_buffer_snapshot
11833 .ranges_to_buffer_ranges(self.selections.disjoint_anchor_ranges())
11834 .map(|(snapshot, _, _)| snapshot.remote_id())
11835 .collect();
11836 for buffer_id in buffer_ids {
11837 self.unfold_buffer(buffer_id, cx);
11838 }
11839 }
11840 }
11841
11842 pub fn unfold_recursive(
11843 &mut self,
11844 _: &UnfoldRecursive,
11845 _window: &mut Window,
11846 cx: &mut Context<Self>,
11847 ) {
11848 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11849 let selections = self.selections.all::<Point>(cx);
11850 let ranges = selections
11851 .iter()
11852 .map(|s| {
11853 let mut range = s.display_range(&display_map).sorted();
11854 *range.start.column_mut() = 0;
11855 *range.end.column_mut() = display_map.line_len(range.end.row());
11856 let start = range.start.to_point(&display_map);
11857 let end = range.end.to_point(&display_map);
11858 start..end
11859 })
11860 .collect::<Vec<_>>();
11861
11862 self.unfold_ranges(&ranges, true, true, cx);
11863 }
11864
11865 pub fn unfold_at(
11866 &mut self,
11867 unfold_at: &UnfoldAt,
11868 _window: &mut Window,
11869 cx: &mut Context<Self>,
11870 ) {
11871 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11872
11873 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
11874 ..Point::new(
11875 unfold_at.buffer_row.0,
11876 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
11877 );
11878
11879 let autoscroll = self
11880 .selections
11881 .all::<Point>(cx)
11882 .iter()
11883 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
11884
11885 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
11886 }
11887
11888 pub fn unfold_all(
11889 &mut self,
11890 _: &actions::UnfoldAll,
11891 _window: &mut Window,
11892 cx: &mut Context<Self>,
11893 ) {
11894 if self.buffer.read(cx).is_singleton() {
11895 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11896 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
11897 } else {
11898 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
11899 editor
11900 .update(&mut cx, |editor, cx| {
11901 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
11902 editor.unfold_buffer(buffer_id, cx);
11903 }
11904 })
11905 .ok();
11906 });
11907 }
11908 }
11909
11910 pub fn fold_selected_ranges(
11911 &mut self,
11912 _: &FoldSelectedRanges,
11913 window: &mut Window,
11914 cx: &mut Context<Self>,
11915 ) {
11916 let selections = self.selections.all::<Point>(cx);
11917 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11918 let line_mode = self.selections.line_mode;
11919 let ranges = selections
11920 .into_iter()
11921 .map(|s| {
11922 if line_mode {
11923 let start = Point::new(s.start.row, 0);
11924 let end = Point::new(
11925 s.end.row,
11926 display_map
11927 .buffer_snapshot
11928 .line_len(MultiBufferRow(s.end.row)),
11929 );
11930 Crease::simple(start..end, display_map.fold_placeholder.clone())
11931 } else {
11932 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
11933 }
11934 })
11935 .collect::<Vec<_>>();
11936 self.fold_creases(ranges, true, window, cx);
11937 }
11938
11939 pub fn fold_ranges<T: ToOffset + Clone>(
11940 &mut self,
11941 ranges: Vec<Range<T>>,
11942 auto_scroll: bool,
11943 window: &mut Window,
11944 cx: &mut Context<Self>,
11945 ) {
11946 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11947 let ranges = ranges
11948 .into_iter()
11949 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
11950 .collect::<Vec<_>>();
11951 self.fold_creases(ranges, auto_scroll, window, cx);
11952 }
11953
11954 pub fn fold_creases<T: ToOffset + Clone>(
11955 &mut self,
11956 creases: Vec<Crease<T>>,
11957 auto_scroll: bool,
11958 window: &mut Window,
11959 cx: &mut Context<Self>,
11960 ) {
11961 if creases.is_empty() {
11962 return;
11963 }
11964
11965 let mut buffers_affected = HashSet::default();
11966 let multi_buffer = self.buffer().read(cx);
11967 for crease in &creases {
11968 if let Some((_, buffer, _)) =
11969 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
11970 {
11971 buffers_affected.insert(buffer.read(cx).remote_id());
11972 };
11973 }
11974
11975 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
11976
11977 if auto_scroll {
11978 self.request_autoscroll(Autoscroll::fit(), cx);
11979 }
11980
11981 cx.notify();
11982
11983 if let Some(active_diagnostics) = self.active_diagnostics.take() {
11984 // Clear diagnostics block when folding a range that contains it.
11985 let snapshot = self.snapshot(window, cx);
11986 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
11987 drop(snapshot);
11988 self.active_diagnostics = Some(active_diagnostics);
11989 self.dismiss_diagnostics(cx);
11990 } else {
11991 self.active_diagnostics = Some(active_diagnostics);
11992 }
11993 }
11994
11995 self.scrollbar_marker_state.dirty = true;
11996 }
11997
11998 /// Removes any folds whose ranges intersect any of the given ranges.
11999 pub fn unfold_ranges<T: ToOffset + Clone>(
12000 &mut self,
12001 ranges: &[Range<T>],
12002 inclusive: bool,
12003 auto_scroll: bool,
12004 cx: &mut Context<Self>,
12005 ) {
12006 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12007 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
12008 });
12009 }
12010
12011 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12012 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
12013 return;
12014 }
12015 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12016 self.display_map
12017 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
12018 cx.emit(EditorEvent::BufferFoldToggled {
12019 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
12020 folded: true,
12021 });
12022 cx.notify();
12023 }
12024
12025 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
12026 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
12027 return;
12028 }
12029 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
12030 self.display_map.update(cx, |display_map, cx| {
12031 display_map.unfold_buffer(buffer_id, cx);
12032 });
12033 cx.emit(EditorEvent::BufferFoldToggled {
12034 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
12035 folded: false,
12036 });
12037 cx.notify();
12038 }
12039
12040 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
12041 self.display_map.read(cx).is_buffer_folded(buffer)
12042 }
12043
12044 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
12045 self.display_map.read(cx).folded_buffers()
12046 }
12047
12048 /// Removes any folds with the given ranges.
12049 pub fn remove_folds_with_type<T: ToOffset + Clone>(
12050 &mut self,
12051 ranges: &[Range<T>],
12052 type_id: TypeId,
12053 auto_scroll: bool,
12054 cx: &mut Context<Self>,
12055 ) {
12056 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
12057 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
12058 });
12059 }
12060
12061 fn remove_folds_with<T: ToOffset + Clone>(
12062 &mut self,
12063 ranges: &[Range<T>],
12064 auto_scroll: bool,
12065 cx: &mut Context<Self>,
12066 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
12067 ) {
12068 if ranges.is_empty() {
12069 return;
12070 }
12071
12072 let mut buffers_affected = HashSet::default();
12073 let multi_buffer = self.buffer().read(cx);
12074 for range in ranges {
12075 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
12076 buffers_affected.insert(buffer.read(cx).remote_id());
12077 };
12078 }
12079
12080 self.display_map.update(cx, update);
12081
12082 if auto_scroll {
12083 self.request_autoscroll(Autoscroll::fit(), cx);
12084 }
12085
12086 cx.notify();
12087 self.scrollbar_marker_state.dirty = true;
12088 self.active_indent_guides_state.dirty = true;
12089 }
12090
12091 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
12092 self.display_map.read(cx).fold_placeholder.clone()
12093 }
12094
12095 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
12096 self.buffer.update(cx, |buffer, cx| {
12097 buffer.set_all_diff_hunks_expanded(cx);
12098 });
12099 }
12100
12101 pub fn expand_all_diff_hunks(
12102 &mut self,
12103 _: &ExpandAllHunkDiffs,
12104 _window: &mut Window,
12105 cx: &mut Context<Self>,
12106 ) {
12107 self.buffer.update(cx, |buffer, cx| {
12108 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
12109 });
12110 }
12111
12112 pub fn toggle_selected_diff_hunks(
12113 &mut self,
12114 _: &ToggleSelectedDiffHunks,
12115 _window: &mut Window,
12116 cx: &mut Context<Self>,
12117 ) {
12118 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12119 self.toggle_diff_hunks_in_ranges(ranges, cx);
12120 }
12121
12122 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
12123 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
12124 self.buffer
12125 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
12126 }
12127
12128 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
12129 self.buffer.update(cx, |buffer, cx| {
12130 let ranges = vec![Anchor::min()..Anchor::max()];
12131 if !buffer.all_diff_hunks_expanded()
12132 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
12133 {
12134 buffer.collapse_diff_hunks(ranges, cx);
12135 true
12136 } else {
12137 false
12138 }
12139 })
12140 }
12141
12142 fn toggle_diff_hunks_in_ranges(
12143 &mut self,
12144 ranges: Vec<Range<Anchor>>,
12145 cx: &mut Context<'_, Editor>,
12146 ) {
12147 self.buffer.update(cx, |buffer, cx| {
12148 if buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx) {
12149 buffer.collapse_diff_hunks(ranges, cx)
12150 } else {
12151 buffer.expand_diff_hunks(ranges, cx)
12152 }
12153 })
12154 }
12155
12156 pub(crate) fn apply_all_diff_hunks(
12157 &mut self,
12158 _: &ApplyAllDiffHunks,
12159 window: &mut Window,
12160 cx: &mut Context<Self>,
12161 ) {
12162 let buffers = self.buffer.read(cx).all_buffers();
12163 for branch_buffer in buffers {
12164 branch_buffer.update(cx, |branch_buffer, cx| {
12165 branch_buffer.merge_into_base(Vec::new(), cx);
12166 });
12167 }
12168
12169 if let Some(project) = self.project.clone() {
12170 self.save(true, project, window, cx).detach_and_log_err(cx);
12171 }
12172 }
12173
12174 pub(crate) fn apply_selected_diff_hunks(
12175 &mut self,
12176 _: &ApplyDiffHunk,
12177 window: &mut Window,
12178 cx: &mut Context<Self>,
12179 ) {
12180 let snapshot = self.snapshot(window, cx);
12181 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx).into_iter());
12182 let mut ranges_by_buffer = HashMap::default();
12183 self.transact(window, cx, |editor, _window, cx| {
12184 for hunk in hunks {
12185 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
12186 ranges_by_buffer
12187 .entry(buffer.clone())
12188 .or_insert_with(Vec::new)
12189 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
12190 }
12191 }
12192
12193 for (buffer, ranges) in ranges_by_buffer {
12194 buffer.update(cx, |buffer, cx| {
12195 buffer.merge_into_base(ranges, cx);
12196 });
12197 }
12198 });
12199
12200 if let Some(project) = self.project.clone() {
12201 self.save(true, project, window, cx).detach_and_log_err(cx);
12202 }
12203 }
12204
12205 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
12206 if hovered != self.gutter_hovered {
12207 self.gutter_hovered = hovered;
12208 cx.notify();
12209 }
12210 }
12211
12212 pub fn insert_blocks(
12213 &mut self,
12214 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
12215 autoscroll: Option<Autoscroll>,
12216 cx: &mut Context<Self>,
12217 ) -> Vec<CustomBlockId> {
12218 let blocks = self
12219 .display_map
12220 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
12221 if let Some(autoscroll) = autoscroll {
12222 self.request_autoscroll(autoscroll, cx);
12223 }
12224 cx.notify();
12225 blocks
12226 }
12227
12228 pub fn resize_blocks(
12229 &mut self,
12230 heights: HashMap<CustomBlockId, u32>,
12231 autoscroll: Option<Autoscroll>,
12232 cx: &mut Context<Self>,
12233 ) {
12234 self.display_map
12235 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
12236 if let Some(autoscroll) = autoscroll {
12237 self.request_autoscroll(autoscroll, cx);
12238 }
12239 cx.notify();
12240 }
12241
12242 pub fn replace_blocks(
12243 &mut self,
12244 renderers: HashMap<CustomBlockId, RenderBlock>,
12245 autoscroll: Option<Autoscroll>,
12246 cx: &mut Context<Self>,
12247 ) {
12248 self.display_map
12249 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
12250 if let Some(autoscroll) = autoscroll {
12251 self.request_autoscroll(autoscroll, cx);
12252 }
12253 cx.notify();
12254 }
12255
12256 pub fn remove_blocks(
12257 &mut self,
12258 block_ids: HashSet<CustomBlockId>,
12259 autoscroll: Option<Autoscroll>,
12260 cx: &mut Context<Self>,
12261 ) {
12262 self.display_map.update(cx, |display_map, cx| {
12263 display_map.remove_blocks(block_ids, cx)
12264 });
12265 if let Some(autoscroll) = autoscroll {
12266 self.request_autoscroll(autoscroll, cx);
12267 }
12268 cx.notify();
12269 }
12270
12271 pub fn row_for_block(
12272 &self,
12273 block_id: CustomBlockId,
12274 cx: &mut Context<Self>,
12275 ) -> Option<DisplayRow> {
12276 self.display_map
12277 .update(cx, |map, cx| map.row_for_block(block_id, cx))
12278 }
12279
12280 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
12281 self.focused_block = Some(focused_block);
12282 }
12283
12284 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
12285 self.focused_block.take()
12286 }
12287
12288 pub fn insert_creases(
12289 &mut self,
12290 creases: impl IntoIterator<Item = Crease<Anchor>>,
12291 cx: &mut Context<Self>,
12292 ) -> Vec<CreaseId> {
12293 self.display_map
12294 .update(cx, |map, cx| map.insert_creases(creases, cx))
12295 }
12296
12297 pub fn remove_creases(
12298 &mut self,
12299 ids: impl IntoIterator<Item = CreaseId>,
12300 cx: &mut Context<Self>,
12301 ) {
12302 self.display_map
12303 .update(cx, |map, cx| map.remove_creases(ids, cx));
12304 }
12305
12306 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
12307 self.display_map
12308 .update(cx, |map, cx| map.snapshot(cx))
12309 .longest_row()
12310 }
12311
12312 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
12313 self.display_map
12314 .update(cx, |map, cx| map.snapshot(cx))
12315 .max_point()
12316 }
12317
12318 pub fn text(&self, cx: &App) -> String {
12319 self.buffer.read(cx).read(cx).text()
12320 }
12321
12322 pub fn is_empty(&self, cx: &App) -> bool {
12323 self.buffer.read(cx).read(cx).is_empty()
12324 }
12325
12326 pub fn text_option(&self, cx: &App) -> Option<String> {
12327 let text = self.text(cx);
12328 let text = text.trim();
12329
12330 if text.is_empty() {
12331 return None;
12332 }
12333
12334 Some(text.to_string())
12335 }
12336
12337 pub fn set_text(
12338 &mut self,
12339 text: impl Into<Arc<str>>,
12340 window: &mut Window,
12341 cx: &mut Context<Self>,
12342 ) {
12343 self.transact(window, cx, |this, _, cx| {
12344 this.buffer
12345 .read(cx)
12346 .as_singleton()
12347 .expect("you can only call set_text on editors for singleton buffers")
12348 .update(cx, |buffer, cx| buffer.set_text(text, cx));
12349 });
12350 }
12351
12352 pub fn display_text(&self, cx: &mut App) -> String {
12353 self.display_map
12354 .update(cx, |map, cx| map.snapshot(cx))
12355 .text()
12356 }
12357
12358 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
12359 let mut wrap_guides = smallvec::smallvec![];
12360
12361 if self.show_wrap_guides == Some(false) {
12362 return wrap_guides;
12363 }
12364
12365 let settings = self.buffer.read(cx).settings_at(0, cx);
12366 if settings.show_wrap_guides {
12367 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
12368 wrap_guides.push((soft_wrap as usize, true));
12369 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
12370 wrap_guides.push((soft_wrap as usize, true));
12371 }
12372 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
12373 }
12374
12375 wrap_guides
12376 }
12377
12378 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
12379 let settings = self.buffer.read(cx).settings_at(0, cx);
12380 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
12381 match mode {
12382 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
12383 SoftWrap::None
12384 }
12385 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
12386 language_settings::SoftWrap::PreferredLineLength => {
12387 SoftWrap::Column(settings.preferred_line_length)
12388 }
12389 language_settings::SoftWrap::Bounded => {
12390 SoftWrap::Bounded(settings.preferred_line_length)
12391 }
12392 }
12393 }
12394
12395 pub fn set_soft_wrap_mode(
12396 &mut self,
12397 mode: language_settings::SoftWrap,
12398
12399 cx: &mut Context<Self>,
12400 ) {
12401 self.soft_wrap_mode_override = Some(mode);
12402 cx.notify();
12403 }
12404
12405 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
12406 self.text_style_refinement = Some(style);
12407 }
12408
12409 /// called by the Element so we know what style we were most recently rendered with.
12410 pub(crate) fn set_style(
12411 &mut self,
12412 style: EditorStyle,
12413 window: &mut Window,
12414 cx: &mut Context<Self>,
12415 ) {
12416 let rem_size = window.rem_size();
12417 self.display_map.update(cx, |map, cx| {
12418 map.set_font(
12419 style.text.font(),
12420 style.text.font_size.to_pixels(rem_size),
12421 cx,
12422 )
12423 });
12424 self.style = Some(style);
12425 }
12426
12427 pub fn style(&self) -> Option<&EditorStyle> {
12428 self.style.as_ref()
12429 }
12430
12431 // Called by the element. This method is not designed to be called outside of the editor
12432 // element's layout code because it does not notify when rewrapping is computed synchronously.
12433 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
12434 self.display_map
12435 .update(cx, |map, cx| map.set_wrap_width(width, cx))
12436 }
12437
12438 pub fn set_soft_wrap(&mut self) {
12439 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
12440 }
12441
12442 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
12443 if self.soft_wrap_mode_override.is_some() {
12444 self.soft_wrap_mode_override.take();
12445 } else {
12446 let soft_wrap = match self.soft_wrap_mode(cx) {
12447 SoftWrap::GitDiff => return,
12448 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
12449 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
12450 language_settings::SoftWrap::None
12451 }
12452 };
12453 self.soft_wrap_mode_override = Some(soft_wrap);
12454 }
12455 cx.notify();
12456 }
12457
12458 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
12459 let Some(workspace) = self.workspace() else {
12460 return;
12461 };
12462 let fs = workspace.read(cx).app_state().fs.clone();
12463 let current_show = TabBarSettings::get_global(cx).show;
12464 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
12465 setting.show = Some(!current_show);
12466 });
12467 }
12468
12469 pub fn toggle_indent_guides(
12470 &mut self,
12471 _: &ToggleIndentGuides,
12472 _: &mut Window,
12473 cx: &mut Context<Self>,
12474 ) {
12475 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
12476 self.buffer
12477 .read(cx)
12478 .settings_at(0, cx)
12479 .indent_guides
12480 .enabled
12481 });
12482 self.show_indent_guides = Some(!currently_enabled);
12483 cx.notify();
12484 }
12485
12486 fn should_show_indent_guides(&self) -> Option<bool> {
12487 self.show_indent_guides
12488 }
12489
12490 pub fn toggle_line_numbers(
12491 &mut self,
12492 _: &ToggleLineNumbers,
12493 _: &mut Window,
12494 cx: &mut Context<Self>,
12495 ) {
12496 let mut editor_settings = EditorSettings::get_global(cx).clone();
12497 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
12498 EditorSettings::override_global(editor_settings, cx);
12499 }
12500
12501 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
12502 self.use_relative_line_numbers
12503 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
12504 }
12505
12506 pub fn toggle_relative_line_numbers(
12507 &mut self,
12508 _: &ToggleRelativeLineNumbers,
12509 _: &mut Window,
12510 cx: &mut Context<Self>,
12511 ) {
12512 let is_relative = self.should_use_relative_line_numbers(cx);
12513 self.set_relative_line_number(Some(!is_relative), cx)
12514 }
12515
12516 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
12517 self.use_relative_line_numbers = is_relative;
12518 cx.notify();
12519 }
12520
12521 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
12522 self.show_gutter = show_gutter;
12523 cx.notify();
12524 }
12525
12526 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
12527 self.show_scrollbars = show_scrollbars;
12528 cx.notify();
12529 }
12530
12531 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
12532 self.show_line_numbers = Some(show_line_numbers);
12533 cx.notify();
12534 }
12535
12536 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
12537 self.show_git_diff_gutter = Some(show_git_diff_gutter);
12538 cx.notify();
12539 }
12540
12541 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
12542 self.show_code_actions = Some(show_code_actions);
12543 cx.notify();
12544 }
12545
12546 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
12547 self.show_runnables = Some(show_runnables);
12548 cx.notify();
12549 }
12550
12551 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
12552 if self.display_map.read(cx).masked != masked {
12553 self.display_map.update(cx, |map, _| map.masked = masked);
12554 }
12555 cx.notify()
12556 }
12557
12558 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
12559 self.show_wrap_guides = Some(show_wrap_guides);
12560 cx.notify();
12561 }
12562
12563 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
12564 self.show_indent_guides = Some(show_indent_guides);
12565 cx.notify();
12566 }
12567
12568 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
12569 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
12570 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
12571 if let Some(dir) = file.abs_path(cx).parent() {
12572 return Some(dir.to_owned());
12573 }
12574 }
12575
12576 if let Some(project_path) = buffer.read(cx).project_path(cx) {
12577 return Some(project_path.path.to_path_buf());
12578 }
12579 }
12580
12581 None
12582 }
12583
12584 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
12585 self.active_excerpt(cx)?
12586 .1
12587 .read(cx)
12588 .file()
12589 .and_then(|f| f.as_local())
12590 }
12591
12592 fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
12593 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
12594 let project_path = buffer.read(cx).project_path(cx)?;
12595 let project = self.project.as_ref()?.read(cx);
12596 project.absolute_path(&project_path, cx)
12597 })
12598 }
12599
12600 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
12601 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
12602 let project_path = buffer.read(cx).project_path(cx)?;
12603 let project = self.project.as_ref()?.read(cx);
12604 let entry = project.entry_for_path(&project_path, cx)?;
12605 let path = entry.path.to_path_buf();
12606 Some(path)
12607 })
12608 }
12609
12610 pub fn reveal_in_finder(
12611 &mut self,
12612 _: &RevealInFileManager,
12613 _window: &mut Window,
12614 cx: &mut Context<Self>,
12615 ) {
12616 if let Some(target) = self.target_file(cx) {
12617 cx.reveal_path(&target.abs_path(cx));
12618 }
12619 }
12620
12621 pub fn copy_path(&mut self, _: &CopyPath, _window: &mut Window, cx: &mut Context<Self>) {
12622 if let Some(path) = self.target_file_abs_path(cx) {
12623 if let Some(path) = path.to_str() {
12624 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
12625 }
12626 }
12627 }
12628
12629 pub fn copy_relative_path(
12630 &mut self,
12631 _: &CopyRelativePath,
12632 _window: &mut Window,
12633 cx: &mut Context<Self>,
12634 ) {
12635 if let Some(path) = self.target_file_path(cx) {
12636 if let Some(path) = path.to_str() {
12637 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
12638 }
12639 }
12640 }
12641
12642 pub fn toggle_git_blame(
12643 &mut self,
12644 _: &ToggleGitBlame,
12645 window: &mut Window,
12646 cx: &mut Context<Self>,
12647 ) {
12648 self.show_git_blame_gutter = !self.show_git_blame_gutter;
12649
12650 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
12651 self.start_git_blame(true, window, cx);
12652 }
12653
12654 cx.notify();
12655 }
12656
12657 pub fn toggle_git_blame_inline(
12658 &mut self,
12659 _: &ToggleGitBlameInline,
12660 window: &mut Window,
12661 cx: &mut Context<Self>,
12662 ) {
12663 self.toggle_git_blame_inline_internal(true, window, cx);
12664 cx.notify();
12665 }
12666
12667 pub fn git_blame_inline_enabled(&self) -> bool {
12668 self.git_blame_inline_enabled
12669 }
12670
12671 pub fn toggle_selection_menu(
12672 &mut self,
12673 _: &ToggleSelectionMenu,
12674 _: &mut Window,
12675 cx: &mut Context<Self>,
12676 ) {
12677 self.show_selection_menu = self
12678 .show_selection_menu
12679 .map(|show_selections_menu| !show_selections_menu)
12680 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
12681
12682 cx.notify();
12683 }
12684
12685 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
12686 self.show_selection_menu
12687 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
12688 }
12689
12690 fn start_git_blame(
12691 &mut self,
12692 user_triggered: bool,
12693 window: &mut Window,
12694 cx: &mut Context<Self>,
12695 ) {
12696 if let Some(project) = self.project.as_ref() {
12697 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
12698 return;
12699 };
12700
12701 if buffer.read(cx).file().is_none() {
12702 return;
12703 }
12704
12705 let focused = self.focus_handle(cx).contains_focused(window, cx);
12706
12707 let project = project.clone();
12708 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
12709 self.blame_subscription =
12710 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
12711 self.blame = Some(blame);
12712 }
12713 }
12714
12715 fn toggle_git_blame_inline_internal(
12716 &mut self,
12717 user_triggered: bool,
12718 window: &mut Window,
12719 cx: &mut Context<Self>,
12720 ) {
12721 if self.git_blame_inline_enabled {
12722 self.git_blame_inline_enabled = false;
12723 self.show_git_blame_inline = false;
12724 self.show_git_blame_inline_delay_task.take();
12725 } else {
12726 self.git_blame_inline_enabled = true;
12727 self.start_git_blame_inline(user_triggered, window, cx);
12728 }
12729
12730 cx.notify();
12731 }
12732
12733 fn start_git_blame_inline(
12734 &mut self,
12735 user_triggered: bool,
12736 window: &mut Window,
12737 cx: &mut Context<Self>,
12738 ) {
12739 self.start_git_blame(user_triggered, window, cx);
12740
12741 if ProjectSettings::get_global(cx)
12742 .git
12743 .inline_blame_delay()
12744 .is_some()
12745 {
12746 self.start_inline_blame_timer(window, cx);
12747 } else {
12748 self.show_git_blame_inline = true
12749 }
12750 }
12751
12752 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
12753 self.blame.as_ref()
12754 }
12755
12756 pub fn show_git_blame_gutter(&self) -> bool {
12757 self.show_git_blame_gutter
12758 }
12759
12760 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
12761 self.show_git_blame_gutter && self.has_blame_entries(cx)
12762 }
12763
12764 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
12765 self.show_git_blame_inline
12766 && self.focus_handle.is_focused(window)
12767 && !self.newest_selection_head_on_empty_line(cx)
12768 && self.has_blame_entries(cx)
12769 }
12770
12771 fn has_blame_entries(&self, cx: &App) -> bool {
12772 self.blame()
12773 .map_or(false, |blame| blame.read(cx).has_generated_entries())
12774 }
12775
12776 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
12777 let cursor_anchor = self.selections.newest_anchor().head();
12778
12779 let snapshot = self.buffer.read(cx).snapshot(cx);
12780 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
12781
12782 snapshot.line_len(buffer_row) == 0
12783 }
12784
12785 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
12786 let buffer_and_selection = maybe!({
12787 let selection = self.selections.newest::<Point>(cx);
12788 let selection_range = selection.range();
12789
12790 let multi_buffer = self.buffer().read(cx);
12791 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12792 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
12793
12794 let (buffer, range, _) = if selection.reversed {
12795 buffer_ranges.first()
12796 } else {
12797 buffer_ranges.last()
12798 }?;
12799
12800 let selection = text::ToPoint::to_point(&range.start, &buffer).row
12801 ..text::ToPoint::to_point(&range.end, &buffer).row;
12802 Some((
12803 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
12804 selection,
12805 ))
12806 });
12807
12808 let Some((buffer, selection)) = buffer_and_selection else {
12809 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
12810 };
12811
12812 let Some(project) = self.project.as_ref() else {
12813 return Task::ready(Err(anyhow!("editor does not have project")));
12814 };
12815
12816 project.update(cx, |project, cx| {
12817 project.get_permalink_to_line(&buffer, selection, cx)
12818 })
12819 }
12820
12821 pub fn copy_permalink_to_line(
12822 &mut self,
12823 _: &CopyPermalinkToLine,
12824 window: &mut Window,
12825 cx: &mut Context<Self>,
12826 ) {
12827 let permalink_task = self.get_permalink_to_line(cx);
12828 let workspace = self.workspace();
12829
12830 cx.spawn_in(window, |_, mut cx| async move {
12831 match permalink_task.await {
12832 Ok(permalink) => {
12833 cx.update(|_, cx| {
12834 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
12835 })
12836 .ok();
12837 }
12838 Err(err) => {
12839 let message = format!("Failed to copy permalink: {err}");
12840
12841 Err::<(), anyhow::Error>(err).log_err();
12842
12843 if let Some(workspace) = workspace {
12844 workspace
12845 .update_in(&mut cx, |workspace, _, cx| {
12846 struct CopyPermalinkToLine;
12847
12848 workspace.show_toast(
12849 Toast::new(
12850 NotificationId::unique::<CopyPermalinkToLine>(),
12851 message,
12852 ),
12853 cx,
12854 )
12855 })
12856 .ok();
12857 }
12858 }
12859 }
12860 })
12861 .detach();
12862 }
12863
12864 pub fn copy_file_location(
12865 &mut self,
12866 _: &CopyFileLocation,
12867 _: &mut Window,
12868 cx: &mut Context<Self>,
12869 ) {
12870 let selection = self.selections.newest::<Point>(cx).start.row + 1;
12871 if let Some(file) = self.target_file(cx) {
12872 if let Some(path) = file.path().to_str() {
12873 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
12874 }
12875 }
12876 }
12877
12878 pub fn open_permalink_to_line(
12879 &mut self,
12880 _: &OpenPermalinkToLine,
12881 window: &mut Window,
12882 cx: &mut Context<Self>,
12883 ) {
12884 let permalink_task = self.get_permalink_to_line(cx);
12885 let workspace = self.workspace();
12886
12887 cx.spawn_in(window, |_, mut cx| async move {
12888 match permalink_task.await {
12889 Ok(permalink) => {
12890 cx.update(|_, cx| {
12891 cx.open_url(permalink.as_ref());
12892 })
12893 .ok();
12894 }
12895 Err(err) => {
12896 let message = format!("Failed to open permalink: {err}");
12897
12898 Err::<(), anyhow::Error>(err).log_err();
12899
12900 if let Some(workspace) = workspace {
12901 workspace
12902 .update(&mut cx, |workspace, cx| {
12903 struct OpenPermalinkToLine;
12904
12905 workspace.show_toast(
12906 Toast::new(
12907 NotificationId::unique::<OpenPermalinkToLine>(),
12908 message,
12909 ),
12910 cx,
12911 )
12912 })
12913 .ok();
12914 }
12915 }
12916 }
12917 })
12918 .detach();
12919 }
12920
12921 pub fn insert_uuid_v4(
12922 &mut self,
12923 _: &InsertUuidV4,
12924 window: &mut Window,
12925 cx: &mut Context<Self>,
12926 ) {
12927 self.insert_uuid(UuidVersion::V4, window, cx);
12928 }
12929
12930 pub fn insert_uuid_v7(
12931 &mut self,
12932 _: &InsertUuidV7,
12933 window: &mut Window,
12934 cx: &mut Context<Self>,
12935 ) {
12936 self.insert_uuid(UuidVersion::V7, window, cx);
12937 }
12938
12939 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
12940 self.transact(window, cx, |this, window, cx| {
12941 let edits = this
12942 .selections
12943 .all::<Point>(cx)
12944 .into_iter()
12945 .map(|selection| {
12946 let uuid = match version {
12947 UuidVersion::V4 => uuid::Uuid::new_v4(),
12948 UuidVersion::V7 => uuid::Uuid::now_v7(),
12949 };
12950
12951 (selection.range(), uuid.to_string())
12952 });
12953 this.edit(edits, cx);
12954 this.refresh_inline_completion(true, false, window, cx);
12955 });
12956 }
12957
12958 pub fn open_selections_in_multibuffer(
12959 &mut self,
12960 _: &OpenSelectionsInMultibuffer,
12961 window: &mut Window,
12962 cx: &mut Context<Self>,
12963 ) {
12964 let multibuffer = self.buffer.read(cx);
12965
12966 let Some(buffer) = multibuffer.as_singleton() else {
12967 return;
12968 };
12969
12970 let Some(workspace) = self.workspace() else {
12971 return;
12972 };
12973
12974 let locations = self
12975 .selections
12976 .disjoint_anchors()
12977 .iter()
12978 .map(|range| Location {
12979 buffer: buffer.clone(),
12980 range: range.start.text_anchor..range.end.text_anchor,
12981 })
12982 .collect::<Vec<_>>();
12983
12984 let title = multibuffer.title(cx).to_string();
12985
12986 cx.spawn_in(window, |_, mut cx| async move {
12987 workspace.update_in(&mut cx, |workspace, window, cx| {
12988 Self::open_locations_in_multibuffer(
12989 workspace,
12990 locations,
12991 format!("Selections for '{title}'"),
12992 false,
12993 MultibufferSelectionMode::All,
12994 window,
12995 cx,
12996 );
12997 })
12998 })
12999 .detach();
13000 }
13001
13002 /// Adds a row highlight for the given range. If a row has multiple highlights, the
13003 /// last highlight added will be used.
13004 ///
13005 /// If the range ends at the beginning of a line, then that line will not be highlighted.
13006 pub fn highlight_rows<T: 'static>(
13007 &mut self,
13008 range: Range<Anchor>,
13009 color: Hsla,
13010 should_autoscroll: bool,
13011 cx: &mut Context<Self>,
13012 ) {
13013 let snapshot = self.buffer().read(cx).snapshot(cx);
13014 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13015 let ix = row_highlights.binary_search_by(|highlight| {
13016 Ordering::Equal
13017 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
13018 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
13019 });
13020
13021 if let Err(mut ix) = ix {
13022 let index = post_inc(&mut self.highlight_order);
13023
13024 // If this range intersects with the preceding highlight, then merge it with
13025 // the preceding highlight. Otherwise insert a new highlight.
13026 let mut merged = false;
13027 if ix > 0 {
13028 let prev_highlight = &mut row_highlights[ix - 1];
13029 if prev_highlight
13030 .range
13031 .end
13032 .cmp(&range.start, &snapshot)
13033 .is_ge()
13034 {
13035 ix -= 1;
13036 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
13037 prev_highlight.range.end = range.end;
13038 }
13039 merged = true;
13040 prev_highlight.index = index;
13041 prev_highlight.color = color;
13042 prev_highlight.should_autoscroll = should_autoscroll;
13043 }
13044 }
13045
13046 if !merged {
13047 row_highlights.insert(
13048 ix,
13049 RowHighlight {
13050 range: range.clone(),
13051 index,
13052 color,
13053 should_autoscroll,
13054 },
13055 );
13056 }
13057
13058 // If any of the following highlights intersect with this one, merge them.
13059 while let Some(next_highlight) = row_highlights.get(ix + 1) {
13060 let highlight = &row_highlights[ix];
13061 if next_highlight
13062 .range
13063 .start
13064 .cmp(&highlight.range.end, &snapshot)
13065 .is_le()
13066 {
13067 if next_highlight
13068 .range
13069 .end
13070 .cmp(&highlight.range.end, &snapshot)
13071 .is_gt()
13072 {
13073 row_highlights[ix].range.end = next_highlight.range.end;
13074 }
13075 row_highlights.remove(ix + 1);
13076 } else {
13077 break;
13078 }
13079 }
13080 }
13081 }
13082
13083 /// Remove any highlighted row ranges of the given type that intersect the
13084 /// given ranges.
13085 pub fn remove_highlighted_rows<T: 'static>(
13086 &mut self,
13087 ranges_to_remove: Vec<Range<Anchor>>,
13088 cx: &mut Context<Self>,
13089 ) {
13090 let snapshot = self.buffer().read(cx).snapshot(cx);
13091 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
13092 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
13093 row_highlights.retain(|highlight| {
13094 while let Some(range_to_remove) = ranges_to_remove.peek() {
13095 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
13096 Ordering::Less | Ordering::Equal => {
13097 ranges_to_remove.next();
13098 }
13099 Ordering::Greater => {
13100 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
13101 Ordering::Less | Ordering::Equal => {
13102 return false;
13103 }
13104 Ordering::Greater => break,
13105 }
13106 }
13107 }
13108 }
13109
13110 true
13111 })
13112 }
13113
13114 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
13115 pub fn clear_row_highlights<T: 'static>(&mut self) {
13116 self.highlighted_rows.remove(&TypeId::of::<T>());
13117 }
13118
13119 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
13120 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
13121 self.highlighted_rows
13122 .get(&TypeId::of::<T>())
13123 .map_or(&[] as &[_], |vec| vec.as_slice())
13124 .iter()
13125 .map(|highlight| (highlight.range.clone(), highlight.color))
13126 }
13127
13128 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
13129 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
13130 /// Allows to ignore certain kinds of highlights.
13131 pub fn highlighted_display_rows(
13132 &self,
13133 window: &mut Window,
13134 cx: &mut App,
13135 ) -> BTreeMap<DisplayRow, Hsla> {
13136 let snapshot = self.snapshot(window, cx);
13137 let mut used_highlight_orders = HashMap::default();
13138 self.highlighted_rows
13139 .iter()
13140 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
13141 .fold(
13142 BTreeMap::<DisplayRow, Hsla>::new(),
13143 |mut unique_rows, highlight| {
13144 let start = highlight.range.start.to_display_point(&snapshot);
13145 let end = highlight.range.end.to_display_point(&snapshot);
13146 let start_row = start.row().0;
13147 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
13148 && end.column() == 0
13149 {
13150 end.row().0.saturating_sub(1)
13151 } else {
13152 end.row().0
13153 };
13154 for row in start_row..=end_row {
13155 let used_index =
13156 used_highlight_orders.entry(row).or_insert(highlight.index);
13157 if highlight.index >= *used_index {
13158 *used_index = highlight.index;
13159 unique_rows.insert(DisplayRow(row), highlight.color);
13160 }
13161 }
13162 unique_rows
13163 },
13164 )
13165 }
13166
13167 pub fn highlighted_display_row_for_autoscroll(
13168 &self,
13169 snapshot: &DisplaySnapshot,
13170 ) -> Option<DisplayRow> {
13171 self.highlighted_rows
13172 .values()
13173 .flat_map(|highlighted_rows| highlighted_rows.iter())
13174 .filter_map(|highlight| {
13175 if highlight.should_autoscroll {
13176 Some(highlight.range.start.to_display_point(snapshot).row())
13177 } else {
13178 None
13179 }
13180 })
13181 .min()
13182 }
13183
13184 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
13185 self.highlight_background::<SearchWithinRange>(
13186 ranges,
13187 |colors| colors.editor_document_highlight_read_background,
13188 cx,
13189 )
13190 }
13191
13192 pub fn set_breadcrumb_header(&mut self, new_header: String) {
13193 self.breadcrumb_header = Some(new_header);
13194 }
13195
13196 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
13197 self.clear_background_highlights::<SearchWithinRange>(cx);
13198 }
13199
13200 pub fn highlight_background<T: 'static>(
13201 &mut self,
13202 ranges: &[Range<Anchor>],
13203 color_fetcher: fn(&ThemeColors) -> Hsla,
13204 cx: &mut Context<Self>,
13205 ) {
13206 self.background_highlights
13207 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13208 self.scrollbar_marker_state.dirty = true;
13209 cx.notify();
13210 }
13211
13212 pub fn clear_background_highlights<T: 'static>(
13213 &mut self,
13214 cx: &mut Context<Self>,
13215 ) -> Option<BackgroundHighlight> {
13216 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
13217 if !text_highlights.1.is_empty() {
13218 self.scrollbar_marker_state.dirty = true;
13219 cx.notify();
13220 }
13221 Some(text_highlights)
13222 }
13223
13224 pub fn highlight_gutter<T: 'static>(
13225 &mut self,
13226 ranges: &[Range<Anchor>],
13227 color_fetcher: fn(&App) -> Hsla,
13228 cx: &mut Context<Self>,
13229 ) {
13230 self.gutter_highlights
13231 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
13232 cx.notify();
13233 }
13234
13235 pub fn clear_gutter_highlights<T: 'static>(
13236 &mut self,
13237 cx: &mut Context<Self>,
13238 ) -> Option<GutterHighlight> {
13239 cx.notify();
13240 self.gutter_highlights.remove(&TypeId::of::<T>())
13241 }
13242
13243 #[cfg(feature = "test-support")]
13244 pub fn all_text_background_highlights(
13245 &self,
13246 window: &mut Window,
13247 cx: &mut Context<Self>,
13248 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13249 let snapshot = self.snapshot(window, cx);
13250 let buffer = &snapshot.buffer_snapshot;
13251 let start = buffer.anchor_before(0);
13252 let end = buffer.anchor_after(buffer.len());
13253 let theme = cx.theme().colors();
13254 self.background_highlights_in_range(start..end, &snapshot, theme)
13255 }
13256
13257 #[cfg(feature = "test-support")]
13258 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
13259 let snapshot = self.buffer().read(cx).snapshot(cx);
13260
13261 let highlights = self
13262 .background_highlights
13263 .get(&TypeId::of::<items::BufferSearchHighlights>());
13264
13265 if let Some((_color, ranges)) = highlights {
13266 ranges
13267 .iter()
13268 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
13269 .collect_vec()
13270 } else {
13271 vec![]
13272 }
13273 }
13274
13275 fn document_highlights_for_position<'a>(
13276 &'a self,
13277 position: Anchor,
13278 buffer: &'a MultiBufferSnapshot,
13279 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
13280 let read_highlights = self
13281 .background_highlights
13282 .get(&TypeId::of::<DocumentHighlightRead>())
13283 .map(|h| &h.1);
13284 let write_highlights = self
13285 .background_highlights
13286 .get(&TypeId::of::<DocumentHighlightWrite>())
13287 .map(|h| &h.1);
13288 let left_position = position.bias_left(buffer);
13289 let right_position = position.bias_right(buffer);
13290 read_highlights
13291 .into_iter()
13292 .chain(write_highlights)
13293 .flat_map(move |ranges| {
13294 let start_ix = match ranges.binary_search_by(|probe| {
13295 let cmp = probe.end.cmp(&left_position, buffer);
13296 if cmp.is_ge() {
13297 Ordering::Greater
13298 } else {
13299 Ordering::Less
13300 }
13301 }) {
13302 Ok(i) | Err(i) => i,
13303 };
13304
13305 ranges[start_ix..]
13306 .iter()
13307 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
13308 })
13309 }
13310
13311 pub fn has_background_highlights<T: 'static>(&self) -> bool {
13312 self.background_highlights
13313 .get(&TypeId::of::<T>())
13314 .map_or(false, |(_, highlights)| !highlights.is_empty())
13315 }
13316
13317 pub fn background_highlights_in_range(
13318 &self,
13319 search_range: Range<Anchor>,
13320 display_snapshot: &DisplaySnapshot,
13321 theme: &ThemeColors,
13322 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13323 let mut results = Vec::new();
13324 for (color_fetcher, ranges) in self.background_highlights.values() {
13325 let color = color_fetcher(theme);
13326 let start_ix = match ranges.binary_search_by(|probe| {
13327 let cmp = probe
13328 .end
13329 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13330 if cmp.is_gt() {
13331 Ordering::Greater
13332 } else {
13333 Ordering::Less
13334 }
13335 }) {
13336 Ok(i) | Err(i) => i,
13337 };
13338 for range in &ranges[start_ix..] {
13339 if range
13340 .start
13341 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13342 .is_ge()
13343 {
13344 break;
13345 }
13346
13347 let start = range.start.to_display_point(display_snapshot);
13348 let end = range.end.to_display_point(display_snapshot);
13349 results.push((start..end, color))
13350 }
13351 }
13352 results
13353 }
13354
13355 pub fn background_highlight_row_ranges<T: 'static>(
13356 &self,
13357 search_range: Range<Anchor>,
13358 display_snapshot: &DisplaySnapshot,
13359 count: usize,
13360 ) -> Vec<RangeInclusive<DisplayPoint>> {
13361 let mut results = Vec::new();
13362 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
13363 return vec![];
13364 };
13365
13366 let start_ix = match ranges.binary_search_by(|probe| {
13367 let cmp = probe
13368 .end
13369 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13370 if cmp.is_gt() {
13371 Ordering::Greater
13372 } else {
13373 Ordering::Less
13374 }
13375 }) {
13376 Ok(i) | Err(i) => i,
13377 };
13378 let mut push_region = |start: Option<Point>, end: Option<Point>| {
13379 if let (Some(start_display), Some(end_display)) = (start, end) {
13380 results.push(
13381 start_display.to_display_point(display_snapshot)
13382 ..=end_display.to_display_point(display_snapshot),
13383 );
13384 }
13385 };
13386 let mut start_row: Option<Point> = None;
13387 let mut end_row: Option<Point> = None;
13388 if ranges.len() > count {
13389 return Vec::new();
13390 }
13391 for range in &ranges[start_ix..] {
13392 if range
13393 .start
13394 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13395 .is_ge()
13396 {
13397 break;
13398 }
13399 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
13400 if let Some(current_row) = &end_row {
13401 if end.row == current_row.row {
13402 continue;
13403 }
13404 }
13405 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
13406 if start_row.is_none() {
13407 assert_eq!(end_row, None);
13408 start_row = Some(start);
13409 end_row = Some(end);
13410 continue;
13411 }
13412 if let Some(current_end) = end_row.as_mut() {
13413 if start.row > current_end.row + 1 {
13414 push_region(start_row, end_row);
13415 start_row = Some(start);
13416 end_row = Some(end);
13417 } else {
13418 // Merge two hunks.
13419 *current_end = end;
13420 }
13421 } else {
13422 unreachable!();
13423 }
13424 }
13425 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
13426 push_region(start_row, end_row);
13427 results
13428 }
13429
13430 pub fn gutter_highlights_in_range(
13431 &self,
13432 search_range: Range<Anchor>,
13433 display_snapshot: &DisplaySnapshot,
13434 cx: &App,
13435 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
13436 let mut results = Vec::new();
13437 for (color_fetcher, ranges) in self.gutter_highlights.values() {
13438 let color = color_fetcher(cx);
13439 let start_ix = match ranges.binary_search_by(|probe| {
13440 let cmp = probe
13441 .end
13442 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
13443 if cmp.is_gt() {
13444 Ordering::Greater
13445 } else {
13446 Ordering::Less
13447 }
13448 }) {
13449 Ok(i) | Err(i) => i,
13450 };
13451 for range in &ranges[start_ix..] {
13452 if range
13453 .start
13454 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
13455 .is_ge()
13456 {
13457 break;
13458 }
13459
13460 let start = range.start.to_display_point(display_snapshot);
13461 let end = range.end.to_display_point(display_snapshot);
13462 results.push((start..end, color))
13463 }
13464 }
13465 results
13466 }
13467
13468 /// Get the text ranges corresponding to the redaction query
13469 pub fn redacted_ranges(
13470 &self,
13471 search_range: Range<Anchor>,
13472 display_snapshot: &DisplaySnapshot,
13473 cx: &App,
13474 ) -> Vec<Range<DisplayPoint>> {
13475 display_snapshot
13476 .buffer_snapshot
13477 .redacted_ranges(search_range, |file| {
13478 if let Some(file) = file {
13479 file.is_private()
13480 && EditorSettings::get(
13481 Some(SettingsLocation {
13482 worktree_id: file.worktree_id(cx),
13483 path: file.path().as_ref(),
13484 }),
13485 cx,
13486 )
13487 .redact_private_values
13488 } else {
13489 false
13490 }
13491 })
13492 .map(|range| {
13493 range.start.to_display_point(display_snapshot)
13494 ..range.end.to_display_point(display_snapshot)
13495 })
13496 .collect()
13497 }
13498
13499 pub fn highlight_text<T: 'static>(
13500 &mut self,
13501 ranges: Vec<Range<Anchor>>,
13502 style: HighlightStyle,
13503 cx: &mut Context<Self>,
13504 ) {
13505 self.display_map.update(cx, |map, _| {
13506 map.highlight_text(TypeId::of::<T>(), ranges, style)
13507 });
13508 cx.notify();
13509 }
13510
13511 pub(crate) fn highlight_inlays<T: 'static>(
13512 &mut self,
13513 highlights: Vec<InlayHighlight>,
13514 style: HighlightStyle,
13515 cx: &mut Context<Self>,
13516 ) {
13517 self.display_map.update(cx, |map, _| {
13518 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
13519 });
13520 cx.notify();
13521 }
13522
13523 pub fn text_highlights<'a, T: 'static>(
13524 &'a self,
13525 cx: &'a App,
13526 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
13527 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
13528 }
13529
13530 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
13531 let cleared = self
13532 .display_map
13533 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
13534 if cleared {
13535 cx.notify();
13536 }
13537 }
13538
13539 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
13540 (self.read_only(cx) || self.blink_manager.read(cx).visible())
13541 && self.focus_handle.is_focused(window)
13542 }
13543
13544 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
13545 self.show_cursor_when_unfocused = is_enabled;
13546 cx.notify();
13547 }
13548
13549 pub fn lsp_store(&self, cx: &App) -> Option<Entity<LspStore>> {
13550 self.project
13551 .as_ref()
13552 .map(|project| project.read(cx).lsp_store())
13553 }
13554
13555 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
13556 cx.notify();
13557 }
13558
13559 fn on_buffer_event(
13560 &mut self,
13561 multibuffer: &Entity<MultiBuffer>,
13562 event: &multi_buffer::Event,
13563 window: &mut Window,
13564 cx: &mut Context<Self>,
13565 ) {
13566 match event {
13567 multi_buffer::Event::Edited {
13568 singleton_buffer_edited,
13569 edited_buffer: buffer_edited,
13570 } => {
13571 self.scrollbar_marker_state.dirty = true;
13572 self.active_indent_guides_state.dirty = true;
13573 self.refresh_active_diagnostics(cx);
13574 self.refresh_code_actions(window, cx);
13575 if self.has_active_inline_completion() {
13576 self.update_visible_inline_completion(window, cx);
13577 }
13578 if let Some(buffer) = buffer_edited {
13579 let buffer_id = buffer.read(cx).remote_id();
13580 if !self.registered_buffers.contains_key(&buffer_id) {
13581 if let Some(lsp_store) = self.lsp_store(cx) {
13582 lsp_store.update(cx, |lsp_store, cx| {
13583 self.registered_buffers.insert(
13584 buffer_id,
13585 lsp_store.register_buffer_with_language_servers(&buffer, cx),
13586 );
13587 })
13588 }
13589 }
13590 }
13591 cx.emit(EditorEvent::BufferEdited);
13592 cx.emit(SearchEvent::MatchesInvalidated);
13593 if *singleton_buffer_edited {
13594 if let Some(project) = &self.project {
13595 let project = project.read(cx);
13596 #[allow(clippy::mutable_key_type)]
13597 let languages_affected = multibuffer
13598 .read(cx)
13599 .all_buffers()
13600 .into_iter()
13601 .filter_map(|buffer| {
13602 let buffer = buffer.read(cx);
13603 let language = buffer.language()?;
13604 if project.is_local()
13605 && project
13606 .language_servers_for_local_buffer(buffer, cx)
13607 .count()
13608 == 0
13609 {
13610 None
13611 } else {
13612 Some(language)
13613 }
13614 })
13615 .cloned()
13616 .collect::<HashSet<_>>();
13617 if !languages_affected.is_empty() {
13618 self.refresh_inlay_hints(
13619 InlayHintRefreshReason::BufferEdited(languages_affected),
13620 cx,
13621 );
13622 }
13623 }
13624 }
13625
13626 let Some(project) = &self.project else { return };
13627 let (telemetry, is_via_ssh) = {
13628 let project = project.read(cx);
13629 let telemetry = project.client().telemetry().clone();
13630 let is_via_ssh = project.is_via_ssh();
13631 (telemetry, is_via_ssh)
13632 };
13633 refresh_linked_ranges(self, window, cx);
13634 telemetry.log_edit_event("editor", is_via_ssh);
13635 }
13636 multi_buffer::Event::ExcerptsAdded {
13637 buffer,
13638 predecessor,
13639 excerpts,
13640 } => {
13641 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13642 let buffer_id = buffer.read(cx).remote_id();
13643 if self.buffer.read(cx).change_set_for(buffer_id).is_none() {
13644 if let Some(project) = &self.project {
13645 get_unstaged_changes_for_buffers(
13646 project,
13647 [buffer.clone()],
13648 self.buffer.clone(),
13649 cx,
13650 );
13651 }
13652 }
13653 cx.emit(EditorEvent::ExcerptsAdded {
13654 buffer: buffer.clone(),
13655 predecessor: *predecessor,
13656 excerpts: excerpts.clone(),
13657 });
13658 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
13659 }
13660 multi_buffer::Event::ExcerptsRemoved { ids } => {
13661 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
13662 let buffer = self.buffer.read(cx);
13663 self.registered_buffers
13664 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
13665 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
13666 }
13667 multi_buffer::Event::ExcerptsEdited { ids } => {
13668 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
13669 }
13670 multi_buffer::Event::ExcerptsExpanded { ids } => {
13671 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
13672 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
13673 }
13674 multi_buffer::Event::Reparsed(buffer_id) => {
13675 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13676
13677 cx.emit(EditorEvent::Reparsed(*buffer_id));
13678 }
13679 multi_buffer::Event::DiffHunksToggled => {
13680 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13681 }
13682 multi_buffer::Event::LanguageChanged(buffer_id) => {
13683 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
13684 cx.emit(EditorEvent::Reparsed(*buffer_id));
13685 cx.notify();
13686 }
13687 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
13688 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
13689 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
13690 cx.emit(EditorEvent::TitleChanged)
13691 }
13692 // multi_buffer::Event::DiffBaseChanged => {
13693 // self.scrollbar_marker_state.dirty = true;
13694 // cx.emit(EditorEvent::DiffBaseChanged);
13695 // cx.notify();
13696 // }
13697 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
13698 multi_buffer::Event::DiagnosticsUpdated => {
13699 self.refresh_active_diagnostics(cx);
13700 self.scrollbar_marker_state.dirty = true;
13701 cx.notify();
13702 }
13703 _ => {}
13704 };
13705 }
13706
13707 fn on_display_map_changed(
13708 &mut self,
13709 _: Entity<DisplayMap>,
13710 _: &mut Window,
13711 cx: &mut Context<Self>,
13712 ) {
13713 cx.notify();
13714 }
13715
13716 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
13717 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
13718 self.refresh_inline_completion(true, false, window, cx);
13719 self.refresh_inlay_hints(
13720 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
13721 self.selections.newest_anchor().head(),
13722 &self.buffer.read(cx).snapshot(cx),
13723 cx,
13724 )),
13725 cx,
13726 );
13727
13728 let old_cursor_shape = self.cursor_shape;
13729
13730 {
13731 let editor_settings = EditorSettings::get_global(cx);
13732 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
13733 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
13734 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
13735 }
13736
13737 if old_cursor_shape != self.cursor_shape {
13738 cx.emit(EditorEvent::CursorShapeChanged);
13739 }
13740
13741 let project_settings = ProjectSettings::get_global(cx);
13742 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
13743
13744 if self.mode == EditorMode::Full {
13745 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
13746 if self.git_blame_inline_enabled != inline_blame_enabled {
13747 self.toggle_git_blame_inline_internal(false, window, cx);
13748 }
13749 }
13750
13751 cx.notify();
13752 }
13753
13754 pub fn set_searchable(&mut self, searchable: bool) {
13755 self.searchable = searchable;
13756 }
13757
13758 pub fn searchable(&self) -> bool {
13759 self.searchable
13760 }
13761
13762 fn open_proposed_changes_editor(
13763 &mut self,
13764 _: &OpenProposedChangesEditor,
13765 window: &mut Window,
13766 cx: &mut Context<Self>,
13767 ) {
13768 let Some(workspace) = self.workspace() else {
13769 cx.propagate();
13770 return;
13771 };
13772
13773 let selections = self.selections.all::<usize>(cx);
13774 let multi_buffer = self.buffer.read(cx);
13775 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
13776 let mut new_selections_by_buffer = HashMap::default();
13777 for selection in selections {
13778 for (buffer, range, _) in
13779 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
13780 {
13781 let mut range = range.to_point(buffer);
13782 range.start.column = 0;
13783 range.end.column = buffer.line_len(range.end.row);
13784 new_selections_by_buffer
13785 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
13786 .or_insert(Vec::new())
13787 .push(range)
13788 }
13789 }
13790
13791 let proposed_changes_buffers = new_selections_by_buffer
13792 .into_iter()
13793 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
13794 .collect::<Vec<_>>();
13795 let proposed_changes_editor = cx.new(|cx| {
13796 ProposedChangesEditor::new(
13797 "Proposed changes",
13798 proposed_changes_buffers,
13799 self.project.clone(),
13800 window,
13801 cx,
13802 )
13803 });
13804
13805 window.defer(cx, move |window, cx| {
13806 workspace.update(cx, |workspace, cx| {
13807 workspace.active_pane().update(cx, |pane, cx| {
13808 pane.add_item(
13809 Box::new(proposed_changes_editor),
13810 true,
13811 true,
13812 None,
13813 window,
13814 cx,
13815 );
13816 });
13817 });
13818 });
13819 }
13820
13821 pub fn open_excerpts_in_split(
13822 &mut self,
13823 _: &OpenExcerptsSplit,
13824 window: &mut Window,
13825 cx: &mut Context<Self>,
13826 ) {
13827 self.open_excerpts_common(None, true, window, cx)
13828 }
13829
13830 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
13831 self.open_excerpts_common(None, false, window, cx)
13832 }
13833
13834 fn open_excerpts_common(
13835 &mut self,
13836 jump_data: Option<JumpData>,
13837 split: bool,
13838 window: &mut Window,
13839 cx: &mut Context<Self>,
13840 ) {
13841 let Some(workspace) = self.workspace() else {
13842 cx.propagate();
13843 return;
13844 };
13845
13846 if self.buffer.read(cx).is_singleton() {
13847 cx.propagate();
13848 return;
13849 }
13850
13851 let mut new_selections_by_buffer = HashMap::default();
13852 match &jump_data {
13853 Some(JumpData::MultiBufferPoint {
13854 excerpt_id,
13855 position,
13856 anchor,
13857 line_offset_from_top,
13858 }) => {
13859 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13860 if let Some(buffer) = multi_buffer_snapshot
13861 .buffer_id_for_excerpt(*excerpt_id)
13862 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
13863 {
13864 let buffer_snapshot = buffer.read(cx).snapshot();
13865 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
13866 language::ToPoint::to_point(anchor, &buffer_snapshot)
13867 } else {
13868 buffer_snapshot.clip_point(*position, Bias::Left)
13869 };
13870 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
13871 new_selections_by_buffer.insert(
13872 buffer,
13873 (
13874 vec![jump_to_offset..jump_to_offset],
13875 Some(*line_offset_from_top),
13876 ),
13877 );
13878 }
13879 }
13880 Some(JumpData::MultiBufferRow {
13881 row,
13882 line_offset_from_top,
13883 }) => {
13884 let point = MultiBufferPoint::new(row.0, 0);
13885 if let Some((buffer, buffer_point, _)) =
13886 self.buffer.read(cx).point_to_buffer_point(point, cx)
13887 {
13888 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
13889 new_selections_by_buffer
13890 .entry(buffer)
13891 .or_insert((Vec::new(), Some(*line_offset_from_top)))
13892 .0
13893 .push(buffer_offset..buffer_offset)
13894 }
13895 }
13896 None => {
13897 let selections = self.selections.all::<usize>(cx);
13898 let multi_buffer = self.buffer.read(cx);
13899 for selection in selections {
13900 for (buffer, mut range, _) in multi_buffer
13901 .snapshot(cx)
13902 .range_to_buffer_ranges(selection.range())
13903 {
13904 // When editing branch buffers, jump to the corresponding location
13905 // in their base buffer.
13906 let mut buffer_handle = multi_buffer.buffer(buffer.remote_id()).unwrap();
13907 let buffer = buffer_handle.read(cx);
13908 if let Some(base_buffer) = buffer.base_buffer() {
13909 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
13910 buffer_handle = base_buffer;
13911 }
13912
13913 if selection.reversed {
13914 mem::swap(&mut range.start, &mut range.end);
13915 }
13916 new_selections_by_buffer
13917 .entry(buffer_handle)
13918 .or_insert((Vec::new(), None))
13919 .0
13920 .push(range)
13921 }
13922 }
13923 }
13924 }
13925
13926 if new_selections_by_buffer.is_empty() {
13927 return;
13928 }
13929
13930 // We defer the pane interaction because we ourselves are a workspace item
13931 // and activating a new item causes the pane to call a method on us reentrantly,
13932 // which panics if we're on the stack.
13933 window.defer(cx, move |window, cx| {
13934 workspace.update(cx, |workspace, cx| {
13935 let pane = if split {
13936 workspace.adjacent_pane(window, cx)
13937 } else {
13938 workspace.active_pane().clone()
13939 };
13940
13941 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
13942 let editor = buffer
13943 .read(cx)
13944 .file()
13945 .is_none()
13946 .then(|| {
13947 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
13948 // so `workspace.open_project_item` will never find them, always opening a new editor.
13949 // Instead, we try to activate the existing editor in the pane first.
13950 let (editor, pane_item_index) =
13951 pane.read(cx).items().enumerate().find_map(|(i, item)| {
13952 let editor = item.downcast::<Editor>()?;
13953 let singleton_buffer =
13954 editor.read(cx).buffer().read(cx).as_singleton()?;
13955 if singleton_buffer == buffer {
13956 Some((editor, i))
13957 } else {
13958 None
13959 }
13960 })?;
13961 pane.update(cx, |pane, cx| {
13962 pane.activate_item(pane_item_index, true, true, window, cx)
13963 });
13964 Some(editor)
13965 })
13966 .flatten()
13967 .unwrap_or_else(|| {
13968 workspace.open_project_item::<Self>(
13969 pane.clone(),
13970 buffer,
13971 true,
13972 true,
13973 window,
13974 cx,
13975 )
13976 });
13977
13978 editor.update(cx, |editor, cx| {
13979 let autoscroll = match scroll_offset {
13980 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
13981 None => Autoscroll::newest(),
13982 };
13983 let nav_history = editor.nav_history.take();
13984 editor.change_selections(Some(autoscroll), window, cx, |s| {
13985 s.select_ranges(ranges);
13986 });
13987 editor.nav_history = nav_history;
13988 });
13989 }
13990 })
13991 });
13992 }
13993
13994 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
13995 let snapshot = self.buffer.read(cx).read(cx);
13996 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
13997 Some(
13998 ranges
13999 .iter()
14000 .map(move |range| {
14001 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
14002 })
14003 .collect(),
14004 )
14005 }
14006
14007 fn selection_replacement_ranges(
14008 &self,
14009 range: Range<OffsetUtf16>,
14010 cx: &mut App,
14011 ) -> Vec<Range<OffsetUtf16>> {
14012 let selections = self.selections.all::<OffsetUtf16>(cx);
14013 let newest_selection = selections
14014 .iter()
14015 .max_by_key(|selection| selection.id)
14016 .unwrap();
14017 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
14018 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
14019 let snapshot = self.buffer.read(cx).read(cx);
14020 selections
14021 .into_iter()
14022 .map(|mut selection| {
14023 selection.start.0 =
14024 (selection.start.0 as isize).saturating_add(start_delta) as usize;
14025 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
14026 snapshot.clip_offset_utf16(selection.start, Bias::Left)
14027 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
14028 })
14029 .collect()
14030 }
14031
14032 fn report_editor_event(
14033 &self,
14034 event_type: &'static str,
14035 file_extension: Option<String>,
14036 cx: &App,
14037 ) {
14038 if cfg!(any(test, feature = "test-support")) {
14039 return;
14040 }
14041
14042 let Some(project) = &self.project else { return };
14043
14044 // If None, we are in a file without an extension
14045 let file = self
14046 .buffer
14047 .read(cx)
14048 .as_singleton()
14049 .and_then(|b| b.read(cx).file());
14050 let file_extension = file_extension.or(file
14051 .as_ref()
14052 .and_then(|file| Path::new(file.file_name(cx)).extension())
14053 .and_then(|e| e.to_str())
14054 .map(|a| a.to_string()));
14055
14056 let vim_mode = cx
14057 .global::<SettingsStore>()
14058 .raw_user_settings()
14059 .get("vim_mode")
14060 == Some(&serde_json::Value::Bool(true));
14061
14062 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
14063 == language::language_settings::InlineCompletionProvider::Copilot;
14064 let copilot_enabled_for_language = self
14065 .buffer
14066 .read(cx)
14067 .settings_at(0, cx)
14068 .show_inline_completions;
14069
14070 let project = project.read(cx);
14071 telemetry::event!(
14072 event_type,
14073 file_extension,
14074 vim_mode,
14075 copilot_enabled,
14076 copilot_enabled_for_language,
14077 is_via_ssh = project.is_via_ssh(),
14078 );
14079 }
14080
14081 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
14082 /// with each line being an array of {text, highlight} objects.
14083 fn copy_highlight_json(
14084 &mut self,
14085 _: &CopyHighlightJson,
14086 window: &mut Window,
14087 cx: &mut Context<Self>,
14088 ) {
14089 #[derive(Serialize)]
14090 struct Chunk<'a> {
14091 text: String,
14092 highlight: Option<&'a str>,
14093 }
14094
14095 let snapshot = self.buffer.read(cx).snapshot(cx);
14096 let range = self
14097 .selected_text_range(false, window, cx)
14098 .and_then(|selection| {
14099 if selection.range.is_empty() {
14100 None
14101 } else {
14102 Some(selection.range)
14103 }
14104 })
14105 .unwrap_or_else(|| 0..snapshot.len());
14106
14107 let chunks = snapshot.chunks(range, true);
14108 let mut lines = Vec::new();
14109 let mut line: VecDeque<Chunk> = VecDeque::new();
14110
14111 let Some(style) = self.style.as_ref() else {
14112 return;
14113 };
14114
14115 for chunk in chunks {
14116 let highlight = chunk
14117 .syntax_highlight_id
14118 .and_then(|id| id.name(&style.syntax));
14119 let mut chunk_lines = chunk.text.split('\n').peekable();
14120 while let Some(text) = chunk_lines.next() {
14121 let mut merged_with_last_token = false;
14122 if let Some(last_token) = line.back_mut() {
14123 if last_token.highlight == highlight {
14124 last_token.text.push_str(text);
14125 merged_with_last_token = true;
14126 }
14127 }
14128
14129 if !merged_with_last_token {
14130 line.push_back(Chunk {
14131 text: text.into(),
14132 highlight,
14133 });
14134 }
14135
14136 if chunk_lines.peek().is_some() {
14137 if line.len() > 1 && line.front().unwrap().text.is_empty() {
14138 line.pop_front();
14139 }
14140 if line.len() > 1 && line.back().unwrap().text.is_empty() {
14141 line.pop_back();
14142 }
14143
14144 lines.push(mem::take(&mut line));
14145 }
14146 }
14147 }
14148
14149 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
14150 return;
14151 };
14152 cx.write_to_clipboard(ClipboardItem::new_string(lines));
14153 }
14154
14155 pub fn open_context_menu(
14156 &mut self,
14157 _: &OpenContextMenu,
14158 window: &mut Window,
14159 cx: &mut Context<Self>,
14160 ) {
14161 self.request_autoscroll(Autoscroll::newest(), cx);
14162 let position = self.selections.newest_display(cx).start;
14163 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
14164 }
14165
14166 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
14167 &self.inlay_hint_cache
14168 }
14169
14170 pub fn replay_insert_event(
14171 &mut self,
14172 text: &str,
14173 relative_utf16_range: Option<Range<isize>>,
14174 window: &mut Window,
14175 cx: &mut Context<Self>,
14176 ) {
14177 if !self.input_enabled {
14178 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14179 return;
14180 }
14181 if let Some(relative_utf16_range) = relative_utf16_range {
14182 let selections = self.selections.all::<OffsetUtf16>(cx);
14183 self.change_selections(None, window, cx, |s| {
14184 let new_ranges = selections.into_iter().map(|range| {
14185 let start = OffsetUtf16(
14186 range
14187 .head()
14188 .0
14189 .saturating_add_signed(relative_utf16_range.start),
14190 );
14191 let end = OffsetUtf16(
14192 range
14193 .head()
14194 .0
14195 .saturating_add_signed(relative_utf16_range.end),
14196 );
14197 start..end
14198 });
14199 s.select_ranges(new_ranges);
14200 });
14201 }
14202
14203 self.handle_input(text, window, cx);
14204 }
14205
14206 pub fn supports_inlay_hints(&self, cx: &App) -> bool {
14207 let Some(provider) = self.semantics_provider.as_ref() else {
14208 return false;
14209 };
14210
14211 let mut supports = false;
14212 self.buffer().read(cx).for_each_buffer(|buffer| {
14213 supports |= provider.supports_inlay_hints(buffer, cx);
14214 });
14215 supports
14216 }
14217 pub fn is_focused(&self, window: &mut Window) -> bool {
14218 self.focus_handle.is_focused(window)
14219 }
14220
14221 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14222 cx.emit(EditorEvent::Focused);
14223
14224 if let Some(descendant) = self
14225 .last_focused_descendant
14226 .take()
14227 .and_then(|descendant| descendant.upgrade())
14228 {
14229 window.focus(&descendant);
14230 } else {
14231 if let Some(blame) = self.blame.as_ref() {
14232 blame.update(cx, GitBlame::focus)
14233 }
14234
14235 self.blink_manager.update(cx, BlinkManager::enable);
14236 self.show_cursor_names(window, cx);
14237 self.buffer.update(cx, |buffer, cx| {
14238 buffer.finalize_last_transaction(cx);
14239 if self.leader_peer_id.is_none() {
14240 buffer.set_active_selections(
14241 &self.selections.disjoint_anchors(),
14242 self.selections.line_mode,
14243 self.cursor_shape,
14244 cx,
14245 );
14246 }
14247 });
14248 }
14249 }
14250
14251 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
14252 cx.emit(EditorEvent::FocusedIn)
14253 }
14254
14255 fn handle_focus_out(
14256 &mut self,
14257 event: FocusOutEvent,
14258 _window: &mut Window,
14259 _cx: &mut Context<Self>,
14260 ) {
14261 if event.blurred != self.focus_handle {
14262 self.last_focused_descendant = Some(event.blurred);
14263 }
14264 }
14265
14266 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
14267 self.blink_manager.update(cx, BlinkManager::disable);
14268 self.buffer
14269 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
14270
14271 if let Some(blame) = self.blame.as_ref() {
14272 blame.update(cx, GitBlame::blur)
14273 }
14274 if !self.hover_state.focused(window, cx) {
14275 hide_hover(self, cx);
14276 }
14277
14278 self.hide_context_menu(window, cx);
14279 cx.emit(EditorEvent::Blurred);
14280 cx.notify();
14281 }
14282
14283 pub fn register_action<A: Action>(
14284 &mut self,
14285 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
14286 ) -> Subscription {
14287 let id = self.next_editor_action_id.post_inc();
14288 let listener = Arc::new(listener);
14289 self.editor_actions.borrow_mut().insert(
14290 id,
14291 Box::new(move |window, _| {
14292 let listener = listener.clone();
14293 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
14294 let action = action.downcast_ref().unwrap();
14295 if phase == DispatchPhase::Bubble {
14296 listener(action, window, cx)
14297 }
14298 })
14299 }),
14300 );
14301
14302 let editor_actions = self.editor_actions.clone();
14303 Subscription::new(move || {
14304 editor_actions.borrow_mut().remove(&id);
14305 })
14306 }
14307
14308 pub fn file_header_size(&self) -> u32 {
14309 FILE_HEADER_HEIGHT
14310 }
14311
14312 pub fn revert(
14313 &mut self,
14314 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
14315 window: &mut Window,
14316 cx: &mut Context<Self>,
14317 ) {
14318 self.buffer().update(cx, |multi_buffer, cx| {
14319 for (buffer_id, changes) in revert_changes {
14320 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
14321 buffer.update(cx, |buffer, cx| {
14322 buffer.edit(
14323 changes.into_iter().map(|(range, text)| {
14324 (range, text.to_string().map(Arc::<str>::from))
14325 }),
14326 None,
14327 cx,
14328 );
14329 });
14330 }
14331 }
14332 });
14333 self.change_selections(None, window, cx, |selections| selections.refresh());
14334 }
14335
14336 pub fn to_pixel_point(
14337 &self,
14338 source: multi_buffer::Anchor,
14339 editor_snapshot: &EditorSnapshot,
14340 window: &mut Window,
14341 ) -> Option<gpui::Point<Pixels>> {
14342 let source_point = source.to_display_point(editor_snapshot);
14343 self.display_to_pixel_point(source_point, editor_snapshot, window)
14344 }
14345
14346 pub fn display_to_pixel_point(
14347 &self,
14348 source: DisplayPoint,
14349 editor_snapshot: &EditorSnapshot,
14350 window: &mut Window,
14351 ) -> Option<gpui::Point<Pixels>> {
14352 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
14353 let text_layout_details = self.text_layout_details(window);
14354 let scroll_top = text_layout_details
14355 .scroll_anchor
14356 .scroll_position(editor_snapshot)
14357 .y;
14358
14359 if source.row().as_f32() < scroll_top.floor() {
14360 return None;
14361 }
14362 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
14363 let source_y = line_height * (source.row().as_f32() - scroll_top);
14364 Some(gpui::Point::new(source_x, source_y))
14365 }
14366
14367 pub fn has_active_completions_menu(&self) -> bool {
14368 self.context_menu.borrow().as_ref().map_or(false, |menu| {
14369 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
14370 })
14371 }
14372
14373 pub fn register_addon<T: Addon>(&mut self, instance: T) {
14374 self.addons
14375 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
14376 }
14377
14378 pub fn unregister_addon<T: Addon>(&mut self) {
14379 self.addons.remove(&std::any::TypeId::of::<T>());
14380 }
14381
14382 pub fn addon<T: Addon>(&self) -> Option<&T> {
14383 let type_id = std::any::TypeId::of::<T>();
14384 self.addons
14385 .get(&type_id)
14386 .and_then(|item| item.to_any().downcast_ref::<T>())
14387 }
14388
14389 fn character_size(&self, window: &mut Window) -> gpui::Point<Pixels> {
14390 let text_layout_details = self.text_layout_details(window);
14391 let style = &text_layout_details.editor_style;
14392 let font_id = window.text_system().resolve_font(&style.text.font());
14393 let font_size = style.text.font_size.to_pixels(window.rem_size());
14394 let line_height = style.text.line_height_in_pixels(window.rem_size());
14395 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
14396
14397 gpui::Point::new(em_width, line_height)
14398 }
14399}
14400
14401fn get_unstaged_changes_for_buffers(
14402 project: &Entity<Project>,
14403 buffers: impl IntoIterator<Item = Entity<Buffer>>,
14404 buffer: Entity<MultiBuffer>,
14405 cx: &mut App,
14406) {
14407 let mut tasks = Vec::new();
14408 project.update(cx, |project, cx| {
14409 for buffer in buffers {
14410 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
14411 }
14412 });
14413 cx.spawn(|mut cx| async move {
14414 let change_sets = futures::future::join_all(tasks).await;
14415 buffer
14416 .update(&mut cx, |buffer, cx| {
14417 for change_set in change_sets {
14418 if let Some(change_set) = change_set.log_err() {
14419 buffer.add_change_set(change_set, cx);
14420 }
14421 }
14422 })
14423 .ok();
14424 })
14425 .detach();
14426}
14427
14428fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
14429 let tab_size = tab_size.get() as usize;
14430 let mut width = offset;
14431
14432 for ch in text.chars() {
14433 width += if ch == '\t' {
14434 tab_size - (width % tab_size)
14435 } else {
14436 1
14437 };
14438 }
14439
14440 width - offset
14441}
14442
14443#[cfg(test)]
14444mod tests {
14445 use super::*;
14446
14447 #[test]
14448 fn test_string_size_with_expanded_tabs() {
14449 let nz = |val| NonZeroU32::new(val).unwrap();
14450 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
14451 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
14452 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
14453 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
14454 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
14455 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
14456 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
14457 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
14458 }
14459}
14460
14461/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
14462struct WordBreakingTokenizer<'a> {
14463 input: &'a str,
14464}
14465
14466impl<'a> WordBreakingTokenizer<'a> {
14467 fn new(input: &'a str) -> Self {
14468 Self { input }
14469 }
14470}
14471
14472fn is_char_ideographic(ch: char) -> bool {
14473 use unicode_script::Script::*;
14474 use unicode_script::UnicodeScript;
14475 matches!(ch.script(), Han | Tangut | Yi)
14476}
14477
14478fn is_grapheme_ideographic(text: &str) -> bool {
14479 text.chars().any(is_char_ideographic)
14480}
14481
14482fn is_grapheme_whitespace(text: &str) -> bool {
14483 text.chars().any(|x| x.is_whitespace())
14484}
14485
14486fn should_stay_with_preceding_ideograph(text: &str) -> bool {
14487 text.chars().next().map_or(false, |ch| {
14488 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
14489 })
14490}
14491
14492#[derive(PartialEq, Eq, Debug, Clone, Copy)]
14493struct WordBreakToken<'a> {
14494 token: &'a str,
14495 grapheme_len: usize,
14496 is_whitespace: bool,
14497}
14498
14499impl<'a> Iterator for WordBreakingTokenizer<'a> {
14500 /// Yields a span, the count of graphemes in the token, and whether it was
14501 /// whitespace. Note that it also breaks at word boundaries.
14502 type Item = WordBreakToken<'a>;
14503
14504 fn next(&mut self) -> Option<Self::Item> {
14505 use unicode_segmentation::UnicodeSegmentation;
14506 if self.input.is_empty() {
14507 return None;
14508 }
14509
14510 let mut iter = self.input.graphemes(true).peekable();
14511 let mut offset = 0;
14512 let mut graphemes = 0;
14513 if let Some(first_grapheme) = iter.next() {
14514 let is_whitespace = is_grapheme_whitespace(first_grapheme);
14515 offset += first_grapheme.len();
14516 graphemes += 1;
14517 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
14518 if let Some(grapheme) = iter.peek().copied() {
14519 if should_stay_with_preceding_ideograph(grapheme) {
14520 offset += grapheme.len();
14521 graphemes += 1;
14522 }
14523 }
14524 } else {
14525 let mut words = self.input[offset..].split_word_bound_indices().peekable();
14526 let mut next_word_bound = words.peek().copied();
14527 if next_word_bound.map_or(false, |(i, _)| i == 0) {
14528 next_word_bound = words.next();
14529 }
14530 while let Some(grapheme) = iter.peek().copied() {
14531 if next_word_bound.map_or(false, |(i, _)| i == offset) {
14532 break;
14533 };
14534 if is_grapheme_whitespace(grapheme) != is_whitespace {
14535 break;
14536 };
14537 offset += grapheme.len();
14538 graphemes += 1;
14539 iter.next();
14540 }
14541 }
14542 let token = &self.input[..offset];
14543 self.input = &self.input[offset..];
14544 if is_whitespace {
14545 Some(WordBreakToken {
14546 token: " ",
14547 grapheme_len: 1,
14548 is_whitespace: true,
14549 })
14550 } else {
14551 Some(WordBreakToken {
14552 token,
14553 grapheme_len: graphemes,
14554 is_whitespace: false,
14555 })
14556 }
14557 } else {
14558 None
14559 }
14560 }
14561}
14562
14563#[test]
14564fn test_word_breaking_tokenizer() {
14565 let tests: &[(&str, &[(&str, usize, bool)])] = &[
14566 ("", &[]),
14567 (" ", &[(" ", 1, true)]),
14568 ("Ʒ", &[("Ʒ", 1, false)]),
14569 ("Ǽ", &[("Ǽ", 1, false)]),
14570 ("⋑", &[("⋑", 1, false)]),
14571 ("⋑⋑", &[("⋑⋑", 2, false)]),
14572 (
14573 "原理,进而",
14574 &[
14575 ("原", 1, false),
14576 ("理,", 2, false),
14577 ("进", 1, false),
14578 ("而", 1, false),
14579 ],
14580 ),
14581 (
14582 "hello world",
14583 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
14584 ),
14585 (
14586 "hello, world",
14587 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
14588 ),
14589 (
14590 " hello world",
14591 &[
14592 (" ", 1, true),
14593 ("hello", 5, false),
14594 (" ", 1, true),
14595 ("world", 5, false),
14596 ],
14597 ),
14598 (
14599 "这是什么 \n 钢笔",
14600 &[
14601 ("这", 1, false),
14602 ("是", 1, false),
14603 ("什", 1, false),
14604 ("么", 1, false),
14605 (" ", 1, true),
14606 ("钢", 1, false),
14607 ("笔", 1, false),
14608 ],
14609 ),
14610 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
14611 ];
14612
14613 for (input, result) in tests {
14614 assert_eq!(
14615 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
14616 result
14617 .iter()
14618 .copied()
14619 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
14620 token,
14621 grapheme_len,
14622 is_whitespace,
14623 })
14624 .collect::<Vec<_>>()
14625 );
14626 }
14627}
14628
14629fn wrap_with_prefix(
14630 line_prefix: String,
14631 unwrapped_text: String,
14632 wrap_column: usize,
14633 tab_size: NonZeroU32,
14634) -> String {
14635 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
14636 let mut wrapped_text = String::new();
14637 let mut current_line = line_prefix.clone();
14638
14639 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
14640 let mut current_line_len = line_prefix_len;
14641 for WordBreakToken {
14642 token,
14643 grapheme_len,
14644 is_whitespace,
14645 } in tokenizer
14646 {
14647 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
14648 wrapped_text.push_str(current_line.trim_end());
14649 wrapped_text.push('\n');
14650 current_line.truncate(line_prefix.len());
14651 current_line_len = line_prefix_len;
14652 if !is_whitespace {
14653 current_line.push_str(token);
14654 current_line_len += grapheme_len;
14655 }
14656 } else if !is_whitespace {
14657 current_line.push_str(token);
14658 current_line_len += grapheme_len;
14659 } else if current_line_len != line_prefix_len {
14660 current_line.push(' ');
14661 current_line_len += 1;
14662 }
14663 }
14664
14665 if !current_line.is_empty() {
14666 wrapped_text.push_str(¤t_line);
14667 }
14668 wrapped_text
14669}
14670
14671#[test]
14672fn test_wrap_with_prefix() {
14673 assert_eq!(
14674 wrap_with_prefix(
14675 "# ".to_string(),
14676 "abcdefg".to_string(),
14677 4,
14678 NonZeroU32::new(4).unwrap()
14679 ),
14680 "# abcdefg"
14681 );
14682 assert_eq!(
14683 wrap_with_prefix(
14684 "".to_string(),
14685 "\thello world".to_string(),
14686 8,
14687 NonZeroU32::new(4).unwrap()
14688 ),
14689 "hello\nworld"
14690 );
14691 assert_eq!(
14692 wrap_with_prefix(
14693 "// ".to_string(),
14694 "xx \nyy zz aa bb cc".to_string(),
14695 12,
14696 NonZeroU32::new(4).unwrap()
14697 ),
14698 "// xx yy zz\n// aa bb cc"
14699 );
14700 assert_eq!(
14701 wrap_with_prefix(
14702 String::new(),
14703 "这是什么 \n 钢笔".to_string(),
14704 3,
14705 NonZeroU32::new(4).unwrap()
14706 ),
14707 "这是什\n么 钢\n笔"
14708 );
14709}
14710
14711pub trait CollaborationHub {
14712 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
14713 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
14714 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
14715}
14716
14717impl CollaborationHub for Entity<Project> {
14718 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
14719 self.read(cx).collaborators()
14720 }
14721
14722 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
14723 self.read(cx).user_store().read(cx).participant_indices()
14724 }
14725
14726 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
14727 let this = self.read(cx);
14728 let user_ids = this.collaborators().values().map(|c| c.user_id);
14729 this.user_store().read_with(cx, |user_store, cx| {
14730 user_store.participant_names(user_ids, cx)
14731 })
14732 }
14733}
14734
14735pub trait SemanticsProvider {
14736 fn hover(
14737 &self,
14738 buffer: &Entity<Buffer>,
14739 position: text::Anchor,
14740 cx: &mut App,
14741 ) -> Option<Task<Vec<project::Hover>>>;
14742
14743 fn inlay_hints(
14744 &self,
14745 buffer_handle: Entity<Buffer>,
14746 range: Range<text::Anchor>,
14747 cx: &mut App,
14748 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
14749
14750 fn resolve_inlay_hint(
14751 &self,
14752 hint: InlayHint,
14753 buffer_handle: Entity<Buffer>,
14754 server_id: LanguageServerId,
14755 cx: &mut App,
14756 ) -> Option<Task<anyhow::Result<InlayHint>>>;
14757
14758 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool;
14759
14760 fn document_highlights(
14761 &self,
14762 buffer: &Entity<Buffer>,
14763 position: text::Anchor,
14764 cx: &mut App,
14765 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
14766
14767 fn definitions(
14768 &self,
14769 buffer: &Entity<Buffer>,
14770 position: text::Anchor,
14771 kind: GotoDefinitionKind,
14772 cx: &mut App,
14773 ) -> Option<Task<Result<Vec<LocationLink>>>>;
14774
14775 fn range_for_rename(
14776 &self,
14777 buffer: &Entity<Buffer>,
14778 position: text::Anchor,
14779 cx: &mut App,
14780 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
14781
14782 fn perform_rename(
14783 &self,
14784 buffer: &Entity<Buffer>,
14785 position: text::Anchor,
14786 new_name: String,
14787 cx: &mut App,
14788 ) -> Option<Task<Result<ProjectTransaction>>>;
14789}
14790
14791pub trait CompletionProvider {
14792 fn completions(
14793 &self,
14794 buffer: &Entity<Buffer>,
14795 buffer_position: text::Anchor,
14796 trigger: CompletionContext,
14797 window: &mut Window,
14798 cx: &mut Context<Editor>,
14799 ) -> Task<Result<Vec<Completion>>>;
14800
14801 fn resolve_completions(
14802 &self,
14803 buffer: Entity<Buffer>,
14804 completion_indices: Vec<usize>,
14805 completions: Rc<RefCell<Box<[Completion]>>>,
14806 cx: &mut Context<Editor>,
14807 ) -> Task<Result<bool>>;
14808
14809 fn apply_additional_edits_for_completion(
14810 &self,
14811 _buffer: Entity<Buffer>,
14812 _completions: Rc<RefCell<Box<[Completion]>>>,
14813 _completion_index: usize,
14814 _push_to_history: bool,
14815 _cx: &mut Context<Editor>,
14816 ) -> Task<Result<Option<language::Transaction>>> {
14817 Task::ready(Ok(None))
14818 }
14819
14820 fn is_completion_trigger(
14821 &self,
14822 buffer: &Entity<Buffer>,
14823 position: language::Anchor,
14824 text: &str,
14825 trigger_in_words: bool,
14826 cx: &mut Context<Editor>,
14827 ) -> bool;
14828
14829 fn sort_completions(&self) -> bool {
14830 true
14831 }
14832}
14833
14834pub trait CodeActionProvider {
14835 fn id(&self) -> Arc<str>;
14836
14837 fn code_actions(
14838 &self,
14839 buffer: &Entity<Buffer>,
14840 range: Range<text::Anchor>,
14841 window: &mut Window,
14842 cx: &mut App,
14843 ) -> Task<Result<Vec<CodeAction>>>;
14844
14845 fn apply_code_action(
14846 &self,
14847 buffer_handle: Entity<Buffer>,
14848 action: CodeAction,
14849 excerpt_id: ExcerptId,
14850 push_to_history: bool,
14851 window: &mut Window,
14852 cx: &mut App,
14853 ) -> Task<Result<ProjectTransaction>>;
14854}
14855
14856impl CodeActionProvider for Entity<Project> {
14857 fn id(&self) -> Arc<str> {
14858 "project".into()
14859 }
14860
14861 fn code_actions(
14862 &self,
14863 buffer: &Entity<Buffer>,
14864 range: Range<text::Anchor>,
14865 _window: &mut Window,
14866 cx: &mut App,
14867 ) -> Task<Result<Vec<CodeAction>>> {
14868 self.update(cx, |project, cx| {
14869 project.code_actions(buffer, range, None, cx)
14870 })
14871 }
14872
14873 fn apply_code_action(
14874 &self,
14875 buffer_handle: Entity<Buffer>,
14876 action: CodeAction,
14877 _excerpt_id: ExcerptId,
14878 push_to_history: bool,
14879 _window: &mut Window,
14880 cx: &mut App,
14881 ) -> Task<Result<ProjectTransaction>> {
14882 self.update(cx, |project, cx| {
14883 project.apply_code_action(buffer_handle, action, push_to_history, cx)
14884 })
14885 }
14886}
14887
14888fn snippet_completions(
14889 project: &Project,
14890 buffer: &Entity<Buffer>,
14891 buffer_position: text::Anchor,
14892 cx: &mut App,
14893) -> Task<Result<Vec<Completion>>> {
14894 let language = buffer.read(cx).language_at(buffer_position);
14895 let language_name = language.as_ref().map(|language| language.lsp_id());
14896 let snippet_store = project.snippets().read(cx);
14897 let snippets = snippet_store.snippets_for(language_name, cx);
14898
14899 if snippets.is_empty() {
14900 return Task::ready(Ok(vec![]));
14901 }
14902 let snapshot = buffer.read(cx).text_snapshot();
14903 let chars: String = snapshot
14904 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
14905 .collect();
14906
14907 let scope = language.map(|language| language.default_scope());
14908 let executor = cx.background_executor().clone();
14909
14910 cx.background_executor().spawn(async move {
14911 let classifier = CharClassifier::new(scope).for_completion(true);
14912 let mut last_word = chars
14913 .chars()
14914 .take_while(|c| classifier.is_word(*c))
14915 .collect::<String>();
14916 last_word = last_word.chars().rev().collect();
14917
14918 if last_word.is_empty() {
14919 return Ok(vec![]);
14920 }
14921
14922 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
14923 let to_lsp = |point: &text::Anchor| {
14924 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
14925 point_to_lsp(end)
14926 };
14927 let lsp_end = to_lsp(&buffer_position);
14928
14929 let candidates = snippets
14930 .iter()
14931 .enumerate()
14932 .flat_map(|(ix, snippet)| {
14933 snippet
14934 .prefix
14935 .iter()
14936 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
14937 })
14938 .collect::<Vec<StringMatchCandidate>>();
14939
14940 let mut matches = fuzzy::match_strings(
14941 &candidates,
14942 &last_word,
14943 last_word.chars().any(|c| c.is_uppercase()),
14944 100,
14945 &Default::default(),
14946 executor,
14947 )
14948 .await;
14949
14950 // Remove all candidates where the query's start does not match the start of any word in the candidate
14951 if let Some(query_start) = last_word.chars().next() {
14952 matches.retain(|string_match| {
14953 split_words(&string_match.string).any(|word| {
14954 // Check that the first codepoint of the word as lowercase matches the first
14955 // codepoint of the query as lowercase
14956 word.chars()
14957 .flat_map(|codepoint| codepoint.to_lowercase())
14958 .zip(query_start.to_lowercase())
14959 .all(|(word_cp, query_cp)| word_cp == query_cp)
14960 })
14961 });
14962 }
14963
14964 let matched_strings = matches
14965 .into_iter()
14966 .map(|m| m.string)
14967 .collect::<HashSet<_>>();
14968
14969 let result: Vec<Completion> = snippets
14970 .into_iter()
14971 .filter_map(|snippet| {
14972 let matching_prefix = snippet
14973 .prefix
14974 .iter()
14975 .find(|prefix| matched_strings.contains(*prefix))?;
14976 let start = as_offset - last_word.len();
14977 let start = snapshot.anchor_before(start);
14978 let range = start..buffer_position;
14979 let lsp_start = to_lsp(&start);
14980 let lsp_range = lsp::Range {
14981 start: lsp_start,
14982 end: lsp_end,
14983 };
14984 Some(Completion {
14985 old_range: range,
14986 new_text: snippet.body.clone(),
14987 resolved: false,
14988 label: CodeLabel {
14989 text: matching_prefix.clone(),
14990 runs: vec![],
14991 filter_range: 0..matching_prefix.len(),
14992 },
14993 server_id: LanguageServerId(usize::MAX),
14994 documentation: snippet
14995 .description
14996 .clone()
14997 .map(CompletionDocumentation::SingleLine),
14998 lsp_completion: lsp::CompletionItem {
14999 label: snippet.prefix.first().unwrap().clone(),
15000 kind: Some(CompletionItemKind::SNIPPET),
15001 label_details: snippet.description.as_ref().map(|description| {
15002 lsp::CompletionItemLabelDetails {
15003 detail: Some(description.clone()),
15004 description: None,
15005 }
15006 }),
15007 insert_text_format: Some(InsertTextFormat::SNIPPET),
15008 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
15009 lsp::InsertReplaceEdit {
15010 new_text: snippet.body.clone(),
15011 insert: lsp_range,
15012 replace: lsp_range,
15013 },
15014 )),
15015 filter_text: Some(snippet.body.clone()),
15016 sort_text: Some(char::MAX.to_string()),
15017 ..Default::default()
15018 },
15019 confirm: None,
15020 })
15021 })
15022 .collect();
15023
15024 Ok(result)
15025 })
15026}
15027
15028impl CompletionProvider for Entity<Project> {
15029 fn completions(
15030 &self,
15031 buffer: &Entity<Buffer>,
15032 buffer_position: text::Anchor,
15033 options: CompletionContext,
15034 _window: &mut Window,
15035 cx: &mut Context<Editor>,
15036 ) -> Task<Result<Vec<Completion>>> {
15037 self.update(cx, |project, cx| {
15038 let snippets = snippet_completions(project, buffer, buffer_position, cx);
15039 let project_completions = project.completions(buffer, buffer_position, options, cx);
15040 cx.background_executor().spawn(async move {
15041 let mut completions = project_completions.await?;
15042 let snippets_completions = snippets.await?;
15043 completions.extend(snippets_completions);
15044 Ok(completions)
15045 })
15046 })
15047 }
15048
15049 fn resolve_completions(
15050 &self,
15051 buffer: Entity<Buffer>,
15052 completion_indices: Vec<usize>,
15053 completions: Rc<RefCell<Box<[Completion]>>>,
15054 cx: &mut Context<Editor>,
15055 ) -> Task<Result<bool>> {
15056 self.update(cx, |project, cx| {
15057 project.lsp_store().update(cx, |lsp_store, cx| {
15058 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
15059 })
15060 })
15061 }
15062
15063 fn apply_additional_edits_for_completion(
15064 &self,
15065 buffer: Entity<Buffer>,
15066 completions: Rc<RefCell<Box<[Completion]>>>,
15067 completion_index: usize,
15068 push_to_history: bool,
15069 cx: &mut Context<Editor>,
15070 ) -> Task<Result<Option<language::Transaction>>> {
15071 self.update(cx, |project, cx| {
15072 project.lsp_store().update(cx, |lsp_store, cx| {
15073 lsp_store.apply_additional_edits_for_completion(
15074 buffer,
15075 completions,
15076 completion_index,
15077 push_to_history,
15078 cx,
15079 )
15080 })
15081 })
15082 }
15083
15084 fn is_completion_trigger(
15085 &self,
15086 buffer: &Entity<Buffer>,
15087 position: language::Anchor,
15088 text: &str,
15089 trigger_in_words: bool,
15090 cx: &mut Context<Editor>,
15091 ) -> bool {
15092 let mut chars = text.chars();
15093 let char = if let Some(char) = chars.next() {
15094 char
15095 } else {
15096 return false;
15097 };
15098 if chars.next().is_some() {
15099 return false;
15100 }
15101
15102 let buffer = buffer.read(cx);
15103 let snapshot = buffer.snapshot();
15104 if !snapshot.settings_at(position, cx).show_completions_on_input {
15105 return false;
15106 }
15107 let classifier = snapshot.char_classifier_at(position).for_completion(true);
15108 if trigger_in_words && classifier.is_word(char) {
15109 return true;
15110 }
15111
15112 buffer.completion_triggers().contains(text)
15113 }
15114}
15115
15116impl SemanticsProvider for Entity<Project> {
15117 fn hover(
15118 &self,
15119 buffer: &Entity<Buffer>,
15120 position: text::Anchor,
15121 cx: &mut App,
15122 ) -> Option<Task<Vec<project::Hover>>> {
15123 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
15124 }
15125
15126 fn document_highlights(
15127 &self,
15128 buffer: &Entity<Buffer>,
15129 position: text::Anchor,
15130 cx: &mut App,
15131 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
15132 Some(self.update(cx, |project, cx| {
15133 project.document_highlights(buffer, position, cx)
15134 }))
15135 }
15136
15137 fn definitions(
15138 &self,
15139 buffer: &Entity<Buffer>,
15140 position: text::Anchor,
15141 kind: GotoDefinitionKind,
15142 cx: &mut App,
15143 ) -> Option<Task<Result<Vec<LocationLink>>>> {
15144 Some(self.update(cx, |project, cx| match kind {
15145 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
15146 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
15147 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
15148 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
15149 }))
15150 }
15151
15152 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
15153 // TODO: make this work for remote projects
15154 self.read(cx)
15155 .language_servers_for_local_buffer(buffer.read(cx), cx)
15156 .any(
15157 |(_, server)| match server.capabilities().inlay_hint_provider {
15158 Some(lsp::OneOf::Left(enabled)) => enabled,
15159 Some(lsp::OneOf::Right(_)) => true,
15160 None => false,
15161 },
15162 )
15163 }
15164
15165 fn inlay_hints(
15166 &self,
15167 buffer_handle: Entity<Buffer>,
15168 range: Range<text::Anchor>,
15169 cx: &mut App,
15170 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
15171 Some(self.update(cx, |project, cx| {
15172 project.inlay_hints(buffer_handle, range, cx)
15173 }))
15174 }
15175
15176 fn resolve_inlay_hint(
15177 &self,
15178 hint: InlayHint,
15179 buffer_handle: Entity<Buffer>,
15180 server_id: LanguageServerId,
15181 cx: &mut App,
15182 ) -> Option<Task<anyhow::Result<InlayHint>>> {
15183 Some(self.update(cx, |project, cx| {
15184 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
15185 }))
15186 }
15187
15188 fn range_for_rename(
15189 &self,
15190 buffer: &Entity<Buffer>,
15191 position: text::Anchor,
15192 cx: &mut App,
15193 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
15194 Some(self.update(cx, |project, cx| {
15195 let buffer = buffer.clone();
15196 let task = project.prepare_rename(buffer.clone(), position, cx);
15197 cx.spawn(|_, mut cx| async move {
15198 Ok(match task.await? {
15199 PrepareRenameResponse::Success(range) => Some(range),
15200 PrepareRenameResponse::InvalidPosition => None,
15201 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
15202 // Fallback on using TreeSitter info to determine identifier range
15203 buffer.update(&mut cx, |buffer, _| {
15204 let snapshot = buffer.snapshot();
15205 let (range, kind) = snapshot.surrounding_word(position);
15206 if kind != Some(CharKind::Word) {
15207 return None;
15208 }
15209 Some(
15210 snapshot.anchor_before(range.start)
15211 ..snapshot.anchor_after(range.end),
15212 )
15213 })?
15214 }
15215 })
15216 })
15217 }))
15218 }
15219
15220 fn perform_rename(
15221 &self,
15222 buffer: &Entity<Buffer>,
15223 position: text::Anchor,
15224 new_name: String,
15225 cx: &mut App,
15226 ) -> Option<Task<Result<ProjectTransaction>>> {
15227 Some(self.update(cx, |project, cx| {
15228 project.perform_rename(buffer.clone(), position, new_name, cx)
15229 }))
15230 }
15231}
15232
15233fn inlay_hint_settings(
15234 location: Anchor,
15235 snapshot: &MultiBufferSnapshot,
15236 cx: &mut Context<Editor>,
15237) -> InlayHintSettings {
15238 let file = snapshot.file_at(location);
15239 let language = snapshot.language_at(location).map(|l| l.name());
15240 language_settings(language, file, cx).inlay_hints
15241}
15242
15243fn consume_contiguous_rows(
15244 contiguous_row_selections: &mut Vec<Selection<Point>>,
15245 selection: &Selection<Point>,
15246 display_map: &DisplaySnapshot,
15247 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
15248) -> (MultiBufferRow, MultiBufferRow) {
15249 contiguous_row_selections.push(selection.clone());
15250 let start_row = MultiBufferRow(selection.start.row);
15251 let mut end_row = ending_row(selection, display_map);
15252
15253 while let Some(next_selection) = selections.peek() {
15254 if next_selection.start.row <= end_row.0 {
15255 end_row = ending_row(next_selection, display_map);
15256 contiguous_row_selections.push(selections.next().unwrap().clone());
15257 } else {
15258 break;
15259 }
15260 }
15261 (start_row, end_row)
15262}
15263
15264fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
15265 if next_selection.end.column > 0 || next_selection.is_empty() {
15266 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
15267 } else {
15268 MultiBufferRow(next_selection.end.row)
15269 }
15270}
15271
15272impl EditorSnapshot {
15273 pub fn remote_selections_in_range<'a>(
15274 &'a self,
15275 range: &'a Range<Anchor>,
15276 collaboration_hub: &dyn CollaborationHub,
15277 cx: &'a App,
15278 ) -> impl 'a + Iterator<Item = RemoteSelection> {
15279 let participant_names = collaboration_hub.user_names(cx);
15280 let participant_indices = collaboration_hub.user_participant_indices(cx);
15281 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
15282 let collaborators_by_replica_id = collaborators_by_peer_id
15283 .iter()
15284 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
15285 .collect::<HashMap<_, _>>();
15286 self.buffer_snapshot
15287 .selections_in_range(range, false)
15288 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
15289 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
15290 let participant_index = participant_indices.get(&collaborator.user_id).copied();
15291 let user_name = participant_names.get(&collaborator.user_id).cloned();
15292 Some(RemoteSelection {
15293 replica_id,
15294 selection,
15295 cursor_shape,
15296 line_mode,
15297 participant_index,
15298 peer_id: collaborator.peer_id,
15299 user_name,
15300 })
15301 })
15302 }
15303
15304 pub fn hunks_for_ranges(
15305 &self,
15306 ranges: impl Iterator<Item = Range<Point>>,
15307 ) -> Vec<MultiBufferDiffHunk> {
15308 let mut hunks = Vec::new();
15309 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
15310 HashMap::default();
15311 for query_range in ranges {
15312 let query_rows =
15313 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
15314 for hunk in self.buffer_snapshot.diff_hunks_in_range(
15315 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
15316 ) {
15317 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
15318 // when the caret is just above or just below the deleted hunk.
15319 let allow_adjacent = hunk.status() == DiffHunkStatus::Removed;
15320 let related_to_selection = if allow_adjacent {
15321 hunk.row_range.overlaps(&query_rows)
15322 || hunk.row_range.start == query_rows.end
15323 || hunk.row_range.end == query_rows.start
15324 } else {
15325 hunk.row_range.overlaps(&query_rows)
15326 };
15327 if related_to_selection {
15328 if !processed_buffer_rows
15329 .entry(hunk.buffer_id)
15330 .or_default()
15331 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
15332 {
15333 continue;
15334 }
15335 hunks.push(hunk);
15336 }
15337 }
15338 }
15339
15340 hunks
15341 }
15342
15343 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
15344 self.display_snapshot.buffer_snapshot.language_at(position)
15345 }
15346
15347 pub fn is_focused(&self) -> bool {
15348 self.is_focused
15349 }
15350
15351 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
15352 self.placeholder_text.as_ref()
15353 }
15354
15355 pub fn scroll_position(&self) -> gpui::Point<f32> {
15356 self.scroll_anchor.scroll_position(&self.display_snapshot)
15357 }
15358
15359 fn gutter_dimensions(
15360 &self,
15361 font_id: FontId,
15362 font_size: Pixels,
15363 max_line_number_width: Pixels,
15364 cx: &App,
15365 ) -> Option<GutterDimensions> {
15366 if !self.show_gutter {
15367 return None;
15368 }
15369
15370 let descent = cx.text_system().descent(font_id, font_size);
15371 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
15372 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
15373
15374 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
15375 matches!(
15376 ProjectSettings::get_global(cx).git.git_gutter,
15377 Some(GitGutterSetting::TrackedFiles)
15378 )
15379 });
15380 let gutter_settings = EditorSettings::get_global(cx).gutter;
15381 let show_line_numbers = self
15382 .show_line_numbers
15383 .unwrap_or(gutter_settings.line_numbers);
15384 let line_gutter_width = if show_line_numbers {
15385 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
15386 let min_width_for_number_on_gutter = em_advance * 4.0;
15387 max_line_number_width.max(min_width_for_number_on_gutter)
15388 } else {
15389 0.0.into()
15390 };
15391
15392 let show_code_actions = self
15393 .show_code_actions
15394 .unwrap_or(gutter_settings.code_actions);
15395
15396 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
15397
15398 let git_blame_entries_width =
15399 self.git_blame_gutter_max_author_length
15400 .map(|max_author_length| {
15401 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
15402
15403 /// The number of characters to dedicate to gaps and margins.
15404 const SPACING_WIDTH: usize = 4;
15405
15406 let max_char_count = max_author_length
15407 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
15408 + ::git::SHORT_SHA_LENGTH
15409 + MAX_RELATIVE_TIMESTAMP.len()
15410 + SPACING_WIDTH;
15411
15412 em_advance * max_char_count
15413 });
15414
15415 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
15416 left_padding += if show_code_actions || show_runnables {
15417 em_width * 3.0
15418 } else if show_git_gutter && show_line_numbers {
15419 em_width * 2.0
15420 } else if show_git_gutter || show_line_numbers {
15421 em_width
15422 } else {
15423 px(0.)
15424 };
15425
15426 let right_padding = if gutter_settings.folds && show_line_numbers {
15427 em_width * 4.0
15428 } else if gutter_settings.folds {
15429 em_width * 3.0
15430 } else if show_line_numbers {
15431 em_width
15432 } else {
15433 px(0.)
15434 };
15435
15436 Some(GutterDimensions {
15437 left_padding,
15438 right_padding,
15439 width: line_gutter_width + left_padding + right_padding,
15440 margin: -descent,
15441 git_blame_entries_width,
15442 })
15443 }
15444
15445 pub fn render_crease_toggle(
15446 &self,
15447 buffer_row: MultiBufferRow,
15448 row_contains_cursor: bool,
15449 editor: Entity<Editor>,
15450 window: &mut Window,
15451 cx: &mut App,
15452 ) -> Option<AnyElement> {
15453 let folded = self.is_line_folded(buffer_row);
15454 let mut is_foldable = false;
15455
15456 if let Some(crease) = self
15457 .crease_snapshot
15458 .query_row(buffer_row, &self.buffer_snapshot)
15459 {
15460 is_foldable = true;
15461 match crease {
15462 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
15463 if let Some(render_toggle) = render_toggle {
15464 let toggle_callback =
15465 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
15466 if folded {
15467 editor.update(cx, |editor, cx| {
15468 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
15469 });
15470 } else {
15471 editor.update(cx, |editor, cx| {
15472 editor.unfold_at(
15473 &crate::UnfoldAt { buffer_row },
15474 window,
15475 cx,
15476 )
15477 });
15478 }
15479 });
15480 return Some((render_toggle)(
15481 buffer_row,
15482 folded,
15483 toggle_callback,
15484 window,
15485 cx,
15486 ));
15487 }
15488 }
15489 }
15490 }
15491
15492 is_foldable |= self.starts_indent(buffer_row);
15493
15494 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
15495 Some(
15496 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
15497 .toggle_state(folded)
15498 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
15499 if folded {
15500 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
15501 } else {
15502 this.fold_at(&FoldAt { buffer_row }, window, cx);
15503 }
15504 }))
15505 .into_any_element(),
15506 )
15507 } else {
15508 None
15509 }
15510 }
15511
15512 pub fn render_crease_trailer(
15513 &self,
15514 buffer_row: MultiBufferRow,
15515 window: &mut Window,
15516 cx: &mut App,
15517 ) -> Option<AnyElement> {
15518 let folded = self.is_line_folded(buffer_row);
15519 if let Crease::Inline { render_trailer, .. } = self
15520 .crease_snapshot
15521 .query_row(buffer_row, &self.buffer_snapshot)?
15522 {
15523 let render_trailer = render_trailer.as_ref()?;
15524 Some(render_trailer(buffer_row, folded, window, cx))
15525 } else {
15526 None
15527 }
15528 }
15529}
15530
15531impl Deref for EditorSnapshot {
15532 type Target = DisplaySnapshot;
15533
15534 fn deref(&self) -> &Self::Target {
15535 &self.display_snapshot
15536 }
15537}
15538
15539#[derive(Clone, Debug, PartialEq, Eq)]
15540pub enum EditorEvent {
15541 InputIgnored {
15542 text: Arc<str>,
15543 },
15544 InputHandled {
15545 utf16_range_to_replace: Option<Range<isize>>,
15546 text: Arc<str>,
15547 },
15548 ExcerptsAdded {
15549 buffer: Entity<Buffer>,
15550 predecessor: ExcerptId,
15551 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
15552 },
15553 ExcerptsRemoved {
15554 ids: Vec<ExcerptId>,
15555 },
15556 BufferFoldToggled {
15557 ids: Vec<ExcerptId>,
15558 folded: bool,
15559 },
15560 ExcerptsEdited {
15561 ids: Vec<ExcerptId>,
15562 },
15563 ExcerptsExpanded {
15564 ids: Vec<ExcerptId>,
15565 },
15566 BufferEdited,
15567 Edited {
15568 transaction_id: clock::Lamport,
15569 },
15570 Reparsed(BufferId),
15571 Focused,
15572 FocusedIn,
15573 Blurred,
15574 DirtyChanged,
15575 Saved,
15576 TitleChanged,
15577 DiffBaseChanged,
15578 SelectionsChanged {
15579 local: bool,
15580 },
15581 ScrollPositionChanged {
15582 local: bool,
15583 autoscroll: bool,
15584 },
15585 Closed,
15586 TransactionUndone {
15587 transaction_id: clock::Lamport,
15588 },
15589 TransactionBegun {
15590 transaction_id: clock::Lamport,
15591 },
15592 Reloaded,
15593 CursorShapeChanged,
15594}
15595
15596impl EventEmitter<EditorEvent> for Editor {}
15597
15598impl Focusable for Editor {
15599 fn focus_handle(&self, _cx: &App) -> FocusHandle {
15600 self.focus_handle.clone()
15601 }
15602}
15603
15604impl Render for Editor {
15605 fn render<'a>(&mut self, _: &mut Window, cx: &mut Context<'a, Self>) -> impl IntoElement {
15606 let settings = ThemeSettings::get_global(cx);
15607
15608 let mut text_style = match self.mode {
15609 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
15610 color: cx.theme().colors().editor_foreground,
15611 font_family: settings.ui_font.family.clone(),
15612 font_features: settings.ui_font.features.clone(),
15613 font_fallbacks: settings.ui_font.fallbacks.clone(),
15614 font_size: rems(0.875).into(),
15615 font_weight: settings.ui_font.weight,
15616 line_height: relative(settings.buffer_line_height.value()),
15617 ..Default::default()
15618 },
15619 EditorMode::Full => TextStyle {
15620 color: cx.theme().colors().editor_foreground,
15621 font_family: settings.buffer_font.family.clone(),
15622 font_features: settings.buffer_font.features.clone(),
15623 font_fallbacks: settings.buffer_font.fallbacks.clone(),
15624 font_size: settings.buffer_font_size().into(),
15625 font_weight: settings.buffer_font.weight,
15626 line_height: relative(settings.buffer_line_height.value()),
15627 ..Default::default()
15628 },
15629 };
15630 if let Some(text_style_refinement) = &self.text_style_refinement {
15631 text_style.refine(text_style_refinement)
15632 }
15633
15634 let background = match self.mode {
15635 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
15636 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
15637 EditorMode::Full => cx.theme().colors().editor_background,
15638 };
15639
15640 EditorElement::new(
15641 &cx.entity(),
15642 EditorStyle {
15643 background,
15644 local_player: cx.theme().players().local(),
15645 text: text_style,
15646 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
15647 syntax: cx.theme().syntax().clone(),
15648 status: cx.theme().status().clone(),
15649 inlay_hints_style: make_inlay_hints_style(cx),
15650 inline_completion_styles: make_suggestion_styles(cx),
15651 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
15652 },
15653 )
15654 }
15655}
15656
15657impl EntityInputHandler for Editor {
15658 fn text_for_range(
15659 &mut self,
15660 range_utf16: Range<usize>,
15661 adjusted_range: &mut Option<Range<usize>>,
15662 _: &mut Window,
15663 cx: &mut Context<Self>,
15664 ) -> Option<String> {
15665 let snapshot = self.buffer.read(cx).read(cx);
15666 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
15667 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
15668 if (start.0..end.0) != range_utf16 {
15669 adjusted_range.replace(start.0..end.0);
15670 }
15671 Some(snapshot.text_for_range(start..end).collect())
15672 }
15673
15674 fn selected_text_range(
15675 &mut self,
15676 ignore_disabled_input: bool,
15677 _: &mut Window,
15678 cx: &mut Context<Self>,
15679 ) -> Option<UTF16Selection> {
15680 // Prevent the IME menu from appearing when holding down an alphabetic key
15681 // while input is disabled.
15682 if !ignore_disabled_input && !self.input_enabled {
15683 return None;
15684 }
15685
15686 let selection = self.selections.newest::<OffsetUtf16>(cx);
15687 let range = selection.range();
15688
15689 Some(UTF16Selection {
15690 range: range.start.0..range.end.0,
15691 reversed: selection.reversed,
15692 })
15693 }
15694
15695 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
15696 let snapshot = self.buffer.read(cx).read(cx);
15697 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
15698 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
15699 }
15700
15701 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
15702 self.clear_highlights::<InputComposition>(cx);
15703 self.ime_transaction.take();
15704 }
15705
15706 fn replace_text_in_range(
15707 &mut self,
15708 range_utf16: Option<Range<usize>>,
15709 text: &str,
15710 window: &mut Window,
15711 cx: &mut Context<Self>,
15712 ) {
15713 if !self.input_enabled {
15714 cx.emit(EditorEvent::InputIgnored { text: text.into() });
15715 return;
15716 }
15717
15718 self.transact(window, cx, |this, window, cx| {
15719 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
15720 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
15721 Some(this.selection_replacement_ranges(range_utf16, cx))
15722 } else {
15723 this.marked_text_ranges(cx)
15724 };
15725
15726 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
15727 let newest_selection_id = this.selections.newest_anchor().id;
15728 this.selections
15729 .all::<OffsetUtf16>(cx)
15730 .iter()
15731 .zip(ranges_to_replace.iter())
15732 .find_map(|(selection, range)| {
15733 if selection.id == newest_selection_id {
15734 Some(
15735 (range.start.0 as isize - selection.head().0 as isize)
15736 ..(range.end.0 as isize - selection.head().0 as isize),
15737 )
15738 } else {
15739 None
15740 }
15741 })
15742 });
15743
15744 cx.emit(EditorEvent::InputHandled {
15745 utf16_range_to_replace: range_to_replace,
15746 text: text.into(),
15747 });
15748
15749 if let Some(new_selected_ranges) = new_selected_ranges {
15750 this.change_selections(None, window, cx, |selections| {
15751 selections.select_ranges(new_selected_ranges)
15752 });
15753 this.backspace(&Default::default(), window, cx);
15754 }
15755
15756 this.handle_input(text, window, cx);
15757 });
15758
15759 if let Some(transaction) = self.ime_transaction {
15760 self.buffer.update(cx, |buffer, cx| {
15761 buffer.group_until_transaction(transaction, cx);
15762 });
15763 }
15764
15765 self.unmark_text(window, cx);
15766 }
15767
15768 fn replace_and_mark_text_in_range(
15769 &mut self,
15770 range_utf16: Option<Range<usize>>,
15771 text: &str,
15772 new_selected_range_utf16: Option<Range<usize>>,
15773 window: &mut Window,
15774 cx: &mut Context<Self>,
15775 ) {
15776 if !self.input_enabled {
15777 return;
15778 }
15779
15780 let transaction = self.transact(window, cx, |this, window, cx| {
15781 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
15782 let snapshot = this.buffer.read(cx).read(cx);
15783 if let Some(relative_range_utf16) = range_utf16.as_ref() {
15784 for marked_range in &mut marked_ranges {
15785 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
15786 marked_range.start.0 += relative_range_utf16.start;
15787 marked_range.start =
15788 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
15789 marked_range.end =
15790 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
15791 }
15792 }
15793 Some(marked_ranges)
15794 } else if let Some(range_utf16) = range_utf16 {
15795 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
15796 Some(this.selection_replacement_ranges(range_utf16, cx))
15797 } else {
15798 None
15799 };
15800
15801 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
15802 let newest_selection_id = this.selections.newest_anchor().id;
15803 this.selections
15804 .all::<OffsetUtf16>(cx)
15805 .iter()
15806 .zip(ranges_to_replace.iter())
15807 .find_map(|(selection, range)| {
15808 if selection.id == newest_selection_id {
15809 Some(
15810 (range.start.0 as isize - selection.head().0 as isize)
15811 ..(range.end.0 as isize - selection.head().0 as isize),
15812 )
15813 } else {
15814 None
15815 }
15816 })
15817 });
15818
15819 cx.emit(EditorEvent::InputHandled {
15820 utf16_range_to_replace: range_to_replace,
15821 text: text.into(),
15822 });
15823
15824 if let Some(ranges) = ranges_to_replace {
15825 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
15826 }
15827
15828 let marked_ranges = {
15829 let snapshot = this.buffer.read(cx).read(cx);
15830 this.selections
15831 .disjoint_anchors()
15832 .iter()
15833 .map(|selection| {
15834 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
15835 })
15836 .collect::<Vec<_>>()
15837 };
15838
15839 if text.is_empty() {
15840 this.unmark_text(window, cx);
15841 } else {
15842 this.highlight_text::<InputComposition>(
15843 marked_ranges.clone(),
15844 HighlightStyle {
15845 underline: Some(UnderlineStyle {
15846 thickness: px(1.),
15847 color: None,
15848 wavy: false,
15849 }),
15850 ..Default::default()
15851 },
15852 cx,
15853 );
15854 }
15855
15856 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
15857 let use_autoclose = this.use_autoclose;
15858 let use_auto_surround = this.use_auto_surround;
15859 this.set_use_autoclose(false);
15860 this.set_use_auto_surround(false);
15861 this.handle_input(text, window, cx);
15862 this.set_use_autoclose(use_autoclose);
15863 this.set_use_auto_surround(use_auto_surround);
15864
15865 if let Some(new_selected_range) = new_selected_range_utf16 {
15866 let snapshot = this.buffer.read(cx).read(cx);
15867 let new_selected_ranges = marked_ranges
15868 .into_iter()
15869 .map(|marked_range| {
15870 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
15871 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
15872 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
15873 snapshot.clip_offset_utf16(new_start, Bias::Left)
15874 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
15875 })
15876 .collect::<Vec<_>>();
15877
15878 drop(snapshot);
15879 this.change_selections(None, window, cx, |selections| {
15880 selections.select_ranges(new_selected_ranges)
15881 });
15882 }
15883 });
15884
15885 self.ime_transaction = self.ime_transaction.or(transaction);
15886 if let Some(transaction) = self.ime_transaction {
15887 self.buffer.update(cx, |buffer, cx| {
15888 buffer.group_until_transaction(transaction, cx);
15889 });
15890 }
15891
15892 if self.text_highlights::<InputComposition>(cx).is_none() {
15893 self.ime_transaction.take();
15894 }
15895 }
15896
15897 fn bounds_for_range(
15898 &mut self,
15899 range_utf16: Range<usize>,
15900 element_bounds: gpui::Bounds<Pixels>,
15901 window: &mut Window,
15902 cx: &mut Context<Self>,
15903 ) -> Option<gpui::Bounds<Pixels>> {
15904 let text_layout_details = self.text_layout_details(window);
15905 let gpui::Point {
15906 x: em_width,
15907 y: line_height,
15908 } = self.character_size(window);
15909
15910 let snapshot = self.snapshot(window, cx);
15911 let scroll_position = snapshot.scroll_position();
15912 let scroll_left = scroll_position.x * em_width;
15913
15914 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
15915 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
15916 + self.gutter_dimensions.width
15917 + self.gutter_dimensions.margin;
15918 let y = line_height * (start.row().as_f32() - scroll_position.y);
15919
15920 Some(Bounds {
15921 origin: element_bounds.origin + point(x, y),
15922 size: size(em_width, line_height),
15923 })
15924 }
15925}
15926
15927trait SelectionExt {
15928 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
15929 fn spanned_rows(
15930 &self,
15931 include_end_if_at_line_start: bool,
15932 map: &DisplaySnapshot,
15933 ) -> Range<MultiBufferRow>;
15934}
15935
15936impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
15937 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
15938 let start = self
15939 .start
15940 .to_point(&map.buffer_snapshot)
15941 .to_display_point(map);
15942 let end = self
15943 .end
15944 .to_point(&map.buffer_snapshot)
15945 .to_display_point(map);
15946 if self.reversed {
15947 end..start
15948 } else {
15949 start..end
15950 }
15951 }
15952
15953 fn spanned_rows(
15954 &self,
15955 include_end_if_at_line_start: bool,
15956 map: &DisplaySnapshot,
15957 ) -> Range<MultiBufferRow> {
15958 let start = self.start.to_point(&map.buffer_snapshot);
15959 let mut end = self.end.to_point(&map.buffer_snapshot);
15960 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
15961 end.row -= 1;
15962 }
15963
15964 let buffer_start = map.prev_line_boundary(start).0;
15965 let buffer_end = map.next_line_boundary(end).0;
15966 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
15967 }
15968}
15969
15970impl<T: InvalidationRegion> InvalidationStack<T> {
15971 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
15972 where
15973 S: Clone + ToOffset,
15974 {
15975 while let Some(region) = self.last() {
15976 let all_selections_inside_invalidation_ranges =
15977 if selections.len() == region.ranges().len() {
15978 selections
15979 .iter()
15980 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
15981 .all(|(selection, invalidation_range)| {
15982 let head = selection.head().to_offset(buffer);
15983 invalidation_range.start <= head && invalidation_range.end >= head
15984 })
15985 } else {
15986 false
15987 };
15988
15989 if all_selections_inside_invalidation_ranges {
15990 break;
15991 } else {
15992 self.pop();
15993 }
15994 }
15995 }
15996}
15997
15998impl<T> Default for InvalidationStack<T> {
15999 fn default() -> Self {
16000 Self(Default::default())
16001 }
16002}
16003
16004impl<T> Deref for InvalidationStack<T> {
16005 type Target = Vec<T>;
16006
16007 fn deref(&self) -> &Self::Target {
16008 &self.0
16009 }
16010}
16011
16012impl<T> DerefMut for InvalidationStack<T> {
16013 fn deref_mut(&mut self) -> &mut Self::Target {
16014 &mut self.0
16015 }
16016}
16017
16018impl InvalidationRegion for SnippetState {
16019 fn ranges(&self) -> &[Range<Anchor>] {
16020 &self.ranges[self.active_index]
16021 }
16022}
16023
16024pub fn diagnostic_block_renderer(
16025 diagnostic: Diagnostic,
16026 max_message_rows: Option<u8>,
16027 allow_closing: bool,
16028 _is_valid: bool,
16029) -> RenderBlock {
16030 let (text_without_backticks, code_ranges) =
16031 highlight_diagnostic_message(&diagnostic, max_message_rows);
16032
16033 Arc::new(move |cx: &mut BlockContext| {
16034 let group_id: SharedString = cx.block_id.to_string().into();
16035
16036 let mut text_style = cx.window.text_style().clone();
16037 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
16038 let theme_settings = ThemeSettings::get_global(cx);
16039 text_style.font_family = theme_settings.buffer_font.family.clone();
16040 text_style.font_style = theme_settings.buffer_font.style;
16041 text_style.font_features = theme_settings.buffer_font.features.clone();
16042 text_style.font_weight = theme_settings.buffer_font.weight;
16043
16044 let multi_line_diagnostic = diagnostic.message.contains('\n');
16045
16046 let buttons = |diagnostic: &Diagnostic| {
16047 if multi_line_diagnostic {
16048 v_flex()
16049 } else {
16050 h_flex()
16051 }
16052 .when(allow_closing, |div| {
16053 div.children(diagnostic.is_primary.then(|| {
16054 IconButton::new("close-block", IconName::XCircle)
16055 .icon_color(Color::Muted)
16056 .size(ButtonSize::Compact)
16057 .style(ButtonStyle::Transparent)
16058 .visible_on_hover(group_id.clone())
16059 .on_click(move |_click, window, cx| {
16060 window.dispatch_action(Box::new(Cancel), cx)
16061 })
16062 .tooltip(|window, cx| {
16063 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
16064 })
16065 }))
16066 })
16067 .child(
16068 IconButton::new("copy-block", IconName::Copy)
16069 .icon_color(Color::Muted)
16070 .size(ButtonSize::Compact)
16071 .style(ButtonStyle::Transparent)
16072 .visible_on_hover(group_id.clone())
16073 .on_click({
16074 let message = diagnostic.message.clone();
16075 move |_click, _, cx| {
16076 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
16077 }
16078 })
16079 .tooltip(Tooltip::text("Copy diagnostic message")),
16080 )
16081 };
16082
16083 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
16084 AvailableSpace::min_size(),
16085 cx.window,
16086 cx.app,
16087 );
16088
16089 h_flex()
16090 .id(cx.block_id)
16091 .group(group_id.clone())
16092 .relative()
16093 .size_full()
16094 .block_mouse_down()
16095 .pl(cx.gutter_dimensions.width)
16096 .w(cx.max_width - cx.gutter_dimensions.full_width())
16097 .child(
16098 div()
16099 .flex()
16100 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
16101 .flex_shrink(),
16102 )
16103 .child(buttons(&diagnostic))
16104 .child(div().flex().flex_shrink_0().child(
16105 StyledText::new(text_without_backticks.clone()).with_highlights(
16106 &text_style,
16107 code_ranges.iter().map(|range| {
16108 (
16109 range.clone(),
16110 HighlightStyle {
16111 font_weight: Some(FontWeight::BOLD),
16112 ..Default::default()
16113 },
16114 )
16115 }),
16116 ),
16117 ))
16118 .into_any_element()
16119 })
16120}
16121
16122fn inline_completion_edit_text(
16123 current_snapshot: &BufferSnapshot,
16124 edits: &[(Range<Anchor>, String)],
16125 edit_preview: &EditPreview,
16126 include_deletions: bool,
16127 cx: &App,
16128) -> HighlightedText {
16129 let edits = edits
16130 .iter()
16131 .map(|(anchor, text)| {
16132 (
16133 anchor.start.text_anchor..anchor.end.text_anchor,
16134 text.clone(),
16135 )
16136 })
16137 .collect::<Vec<_>>();
16138
16139 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
16140}
16141
16142pub fn highlight_diagnostic_message(
16143 diagnostic: &Diagnostic,
16144 mut max_message_rows: Option<u8>,
16145) -> (SharedString, Vec<Range<usize>>) {
16146 let mut text_without_backticks = String::new();
16147 let mut code_ranges = Vec::new();
16148
16149 if let Some(source) = &diagnostic.source {
16150 text_without_backticks.push_str(source);
16151 code_ranges.push(0..source.len());
16152 text_without_backticks.push_str(": ");
16153 }
16154
16155 let mut prev_offset = 0;
16156 let mut in_code_block = false;
16157 let has_row_limit = max_message_rows.is_some();
16158 let mut newline_indices = diagnostic
16159 .message
16160 .match_indices('\n')
16161 .filter(|_| has_row_limit)
16162 .map(|(ix, _)| ix)
16163 .fuse()
16164 .peekable();
16165
16166 for (quote_ix, _) in diagnostic
16167 .message
16168 .match_indices('`')
16169 .chain([(diagnostic.message.len(), "")])
16170 {
16171 let mut first_newline_ix = None;
16172 let mut last_newline_ix = None;
16173 while let Some(newline_ix) = newline_indices.peek() {
16174 if *newline_ix < quote_ix {
16175 if first_newline_ix.is_none() {
16176 first_newline_ix = Some(*newline_ix);
16177 }
16178 last_newline_ix = Some(*newline_ix);
16179
16180 if let Some(rows_left) = &mut max_message_rows {
16181 if *rows_left == 0 {
16182 break;
16183 } else {
16184 *rows_left -= 1;
16185 }
16186 }
16187 let _ = newline_indices.next();
16188 } else {
16189 break;
16190 }
16191 }
16192 let prev_len = text_without_backticks.len();
16193 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
16194 text_without_backticks.push_str(new_text);
16195 if in_code_block {
16196 code_ranges.push(prev_len..text_without_backticks.len());
16197 }
16198 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
16199 in_code_block = !in_code_block;
16200 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
16201 text_without_backticks.push_str("...");
16202 break;
16203 }
16204 }
16205
16206 (text_without_backticks.into(), code_ranges)
16207}
16208
16209fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
16210 match severity {
16211 DiagnosticSeverity::ERROR => colors.error,
16212 DiagnosticSeverity::WARNING => colors.warning,
16213 DiagnosticSeverity::INFORMATION => colors.info,
16214 DiagnosticSeverity::HINT => colors.info,
16215 _ => colors.ignored,
16216 }
16217}
16218
16219pub fn styled_runs_for_code_label<'a>(
16220 label: &'a CodeLabel,
16221 syntax_theme: &'a theme::SyntaxTheme,
16222) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
16223 let fade_out = HighlightStyle {
16224 fade_out: Some(0.35),
16225 ..Default::default()
16226 };
16227
16228 let mut prev_end = label.filter_range.end;
16229 label
16230 .runs
16231 .iter()
16232 .enumerate()
16233 .flat_map(move |(ix, (range, highlight_id))| {
16234 let style = if let Some(style) = highlight_id.style(syntax_theme) {
16235 style
16236 } else {
16237 return Default::default();
16238 };
16239 let mut muted_style = style;
16240 muted_style.highlight(fade_out);
16241
16242 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
16243 if range.start >= label.filter_range.end {
16244 if range.start > prev_end {
16245 runs.push((prev_end..range.start, fade_out));
16246 }
16247 runs.push((range.clone(), muted_style));
16248 } else if range.end <= label.filter_range.end {
16249 runs.push((range.clone(), style));
16250 } else {
16251 runs.push((range.start..label.filter_range.end, style));
16252 runs.push((label.filter_range.end..range.end, muted_style));
16253 }
16254 prev_end = cmp::max(prev_end, range.end);
16255
16256 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
16257 runs.push((prev_end..label.text.len(), fade_out));
16258 }
16259
16260 runs
16261 })
16262}
16263
16264pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
16265 let mut prev_index = 0;
16266 let mut prev_codepoint: Option<char> = None;
16267 text.char_indices()
16268 .chain([(text.len(), '\0')])
16269 .filter_map(move |(index, codepoint)| {
16270 let prev_codepoint = prev_codepoint.replace(codepoint)?;
16271 let is_boundary = index == text.len()
16272 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
16273 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
16274 if is_boundary {
16275 let chunk = &text[prev_index..index];
16276 prev_index = index;
16277 Some(chunk)
16278 } else {
16279 None
16280 }
16281 })
16282}
16283
16284pub trait RangeToAnchorExt: Sized {
16285 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
16286
16287 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
16288 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
16289 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
16290 }
16291}
16292
16293impl<T: ToOffset> RangeToAnchorExt for Range<T> {
16294 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
16295 let start_offset = self.start.to_offset(snapshot);
16296 let end_offset = self.end.to_offset(snapshot);
16297 if start_offset == end_offset {
16298 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
16299 } else {
16300 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
16301 }
16302 }
16303}
16304
16305pub trait RowExt {
16306 fn as_f32(&self) -> f32;
16307
16308 fn next_row(&self) -> Self;
16309
16310 fn previous_row(&self) -> Self;
16311
16312 fn minus(&self, other: Self) -> u32;
16313}
16314
16315impl RowExt for DisplayRow {
16316 fn as_f32(&self) -> f32 {
16317 self.0 as f32
16318 }
16319
16320 fn next_row(&self) -> Self {
16321 Self(self.0 + 1)
16322 }
16323
16324 fn previous_row(&self) -> Self {
16325 Self(self.0.saturating_sub(1))
16326 }
16327
16328 fn minus(&self, other: Self) -> u32 {
16329 self.0 - other.0
16330 }
16331}
16332
16333impl RowExt for MultiBufferRow {
16334 fn as_f32(&self) -> f32 {
16335 self.0 as f32
16336 }
16337
16338 fn next_row(&self) -> Self {
16339 Self(self.0 + 1)
16340 }
16341
16342 fn previous_row(&self) -> Self {
16343 Self(self.0.saturating_sub(1))
16344 }
16345
16346 fn minus(&self, other: Self) -> u32 {
16347 self.0 - other.0
16348 }
16349}
16350
16351trait RowRangeExt {
16352 type Row;
16353
16354 fn len(&self) -> usize;
16355
16356 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
16357}
16358
16359impl RowRangeExt for Range<MultiBufferRow> {
16360 type Row = MultiBufferRow;
16361
16362 fn len(&self) -> usize {
16363 (self.end.0 - self.start.0) as usize
16364 }
16365
16366 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
16367 (self.start.0..self.end.0).map(MultiBufferRow)
16368 }
16369}
16370
16371impl RowRangeExt for Range<DisplayRow> {
16372 type Row = DisplayRow;
16373
16374 fn len(&self) -> usize {
16375 (self.end.0 - self.start.0) as usize
16376 }
16377
16378 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
16379 (self.start.0..self.end.0).map(DisplayRow)
16380 }
16381}
16382
16383/// If select range has more than one line, we
16384/// just point the cursor to range.start.
16385fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
16386 if range.start.row == range.end.row {
16387 range
16388 } else {
16389 range.start..range.start
16390 }
16391}
16392pub struct KillRing(ClipboardItem);
16393impl Global for KillRing {}
16394
16395const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
16396
16397fn all_edits_insertions_or_deletions(
16398 edits: &Vec<(Range<Anchor>, String)>,
16399 snapshot: &MultiBufferSnapshot,
16400) -> bool {
16401 let mut all_insertions = true;
16402 let mut all_deletions = true;
16403
16404 for (range, new_text) in edits.iter() {
16405 let range_is_empty = range.to_offset(&snapshot).is_empty();
16406 let text_is_empty = new_text.is_empty();
16407
16408 if range_is_empty != text_is_empty {
16409 if range_is_empty {
16410 all_deletions = false;
16411 } else {
16412 all_insertions = false;
16413 }
16414 } else {
16415 return false;
16416 }
16417
16418 if !all_insertions && !all_deletions {
16419 return false;
16420 }
16421 }
16422 all_insertions || all_deletions
16423}