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 hunk_diff;
29mod indent_guides;
30mod inlay_hint_cache;
31pub mod items;
32mod linked_editing_ranges;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51use ::git::diff::DiffHunkStatus;
52pub(crate) use actions::*;
53pub use actions::{OpenExcerpts, OpenExcerptsSplit};
54use aho_corasick::AhoCorasick;
55use anyhow::{anyhow, Context as _, Result};
56use blink_manager::BlinkManager;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63pub use editor_settings::{
64 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
65};
66pub use editor_settings_controls::*;
67use element::LineWithInvisibles;
68pub use element::{
69 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
70};
71use futures::{future, FutureExt};
72use fuzzy::StringMatchCandidate;
73
74use code_context_menus::{
75 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
76 CompletionEntry, CompletionsMenu, ContextMenuOrigin,
77};
78use git::blame::GitBlame;
79use gpui::{
80 div, impl_actions, point, prelude::*, px, relative, size, Action, AnyElement, AppContext,
81 AsyncWindowContext, AvailableSpace, Bounds, ClipboardEntry, ClipboardItem, Context,
82 DispatchPhase, ElementId, EventEmitter, FocusHandle, FocusOutEvent, FocusableView, FontId,
83 FontWeight, Global, HighlightStyle, Hsla, InteractiveText, KeyContext, Model, ModelContext,
84 MouseButton, PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText,
85 Subscription, Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
86 UniformListScrollHandle, View, ViewContext, ViewInputHandler, VisualContext, WeakFocusHandle,
87 WeakView, WindowContext,
88};
89use highlight_matching_bracket::refresh_matching_bracket_highlights;
90use hover_popover::{hide_hover, HoverState};
91pub(crate) use hunk_diff::HoveredHunk;
92use hunk_diff::{diff_hunk_to_display, DiffMap, DiffMapSnapshot};
93use indent_guides::ActiveIndentGuidesState;
94use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
95pub use inline_completion::Direction;
96use inline_completion::{InlineCompletionProvider, InlineCompletionProviderHandle};
97pub use items::MAX_TAB_TITLE_LEN;
98use itertools::Itertools;
99use language::{
100 language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
101 markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
102 CursorShape, Diagnostic, DiagnosticEntry, Documentation, IndentKind, IndentSize, Language,
103 OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
104};
105use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
106use linked_editing_ranges::refresh_linked_ranges;
107use mouse_context_menu::MouseContextMenu;
108pub use proposed_changes_editor::{
109 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
110};
111use similar::{ChangeTag, TextDiff};
112use std::iter::Peekable;
113use task::{ResolvedTask, TaskTemplate, TaskVariables};
114
115use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
116pub use lsp::CompletionContext;
117use lsp::{
118 CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity, InsertTextFormat,
119 LanguageServerId, LanguageServerName,
120};
121
122use movement::TextLayoutDetails;
123pub use multi_buffer::{
124 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
125 ToPoint,
126};
127use multi_buffer::{
128 ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
129};
130use project::{
131 buffer_store::BufferChangeSet,
132 lsp_store::{FormatTarget, FormatTrigger, OpenLspBufferHandle},
133 project_settings::{GitGutterSetting, ProjectSettings},
134 CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink,
135 LspStore, Project, ProjectItem, ProjectTransaction, TaskSourceKind,
136};
137use rand::prelude::*;
138use rpc::{proto::*, ErrorExt};
139use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
140use selections_collection::{
141 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
142};
143use serde::{Deserialize, Serialize};
144use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
145use smallvec::SmallVec;
146use snippet::Snippet;
147use std::{
148 any::TypeId,
149 borrow::Cow,
150 cell::RefCell,
151 cmp::{self, Ordering, Reverse},
152 mem,
153 num::NonZeroU32,
154 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
155 path::{Path, PathBuf},
156 rc::Rc,
157 sync::Arc,
158 time::{Duration, Instant},
159};
160pub use sum_tree::Bias;
161use sum_tree::TreeMap;
162use text::{BufferId, OffsetUtf16, Rope};
163use theme::{
164 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
165 ThemeColors, ThemeSettings,
166};
167use ui::{
168 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize,
169 PopoverMenuHandle, Tooltip,
170};
171use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
172use workspace::item::{ItemHandle, PreviewTabsSettings};
173use workspace::notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt};
174use workspace::{
175 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
176};
177use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
178
179use crate::hover_links::{find_url, find_url_from_range};
180use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
181
182pub const FILE_HEADER_HEIGHT: u32 = 2;
183pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
184pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u32 = 1;
185pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
186const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
187const MAX_LINE_LEN: usize = 1024;
188const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
189const MAX_SELECTION_HISTORY_LEN: usize = 1024;
190pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
191#[doc(hidden)]
192pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
193
194pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
195pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
196
197pub fn render_parsed_markdown(
198 element_id: impl Into<ElementId>,
199 parsed: &language::ParsedMarkdown,
200 editor_style: &EditorStyle,
201 workspace: Option<WeakView<Workspace>>,
202 cx: &mut WindowContext,
203) -> InteractiveText {
204 let code_span_background_color = cx
205 .theme()
206 .colors()
207 .editor_document_highlight_read_background;
208
209 let highlights = gpui::combine_highlights(
210 parsed.highlights.iter().filter_map(|(range, highlight)| {
211 let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
212 Some((range.clone(), highlight))
213 }),
214 parsed
215 .regions
216 .iter()
217 .zip(&parsed.region_ranges)
218 .filter_map(|(region, range)| {
219 if region.code {
220 Some((
221 range.clone(),
222 HighlightStyle {
223 background_color: Some(code_span_background_color),
224 ..Default::default()
225 },
226 ))
227 } else {
228 None
229 }
230 }),
231 );
232
233 let mut links = Vec::new();
234 let mut link_ranges = Vec::new();
235 for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
236 if let Some(link) = region.link.clone() {
237 links.push(link);
238 link_ranges.push(range.clone());
239 }
240 }
241
242 InteractiveText::new(
243 element_id,
244 StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
245 )
246 .on_click(link_ranges, move |clicked_range_ix, cx| {
247 match &links[clicked_range_ix] {
248 markdown::Link::Web { url } => cx.open_url(url),
249 markdown::Link::Path { path } => {
250 if let Some(workspace) = &workspace {
251 _ = workspace.update(cx, |workspace, cx| {
252 workspace.open_abs_path(path.clone(), false, cx).detach();
253 });
254 }
255 }
256 }
257 })
258}
259
260#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
261pub enum InlayId {
262 InlineCompletion(usize),
263 Hint(usize),
264}
265
266impl InlayId {
267 fn id(&self) -> usize {
268 match self {
269 Self::InlineCompletion(id) => *id,
270 Self::Hint(id) => *id,
271 }
272 }
273}
274
275enum DiffRowHighlight {}
276enum DocumentHighlightRead {}
277enum DocumentHighlightWrite {}
278enum InputComposition {}
279
280#[derive(Debug, Copy, Clone, PartialEq, Eq)]
281pub enum Navigated {
282 Yes,
283 No,
284}
285
286impl Navigated {
287 pub fn from_bool(yes: bool) -> Navigated {
288 if yes {
289 Navigated::Yes
290 } else {
291 Navigated::No
292 }
293 }
294}
295
296pub fn init_settings(cx: &mut AppContext) {
297 EditorSettings::register(cx);
298}
299
300pub fn init(cx: &mut AppContext) {
301 init_settings(cx);
302
303 workspace::register_project_item::<Editor>(cx);
304 workspace::FollowableViewRegistry::register::<Editor>(cx);
305 workspace::register_serializable_item::<Editor>(cx);
306
307 cx.observe_new_views(
308 |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
309 workspace.register_action(Editor::new_file);
310 workspace.register_action(Editor::new_file_vertical);
311 workspace.register_action(Editor::new_file_horizontal);
312 },
313 )
314 .detach();
315
316 cx.on_action(move |_: &workspace::NewFile, cx| {
317 let app_state = workspace::AppState::global(cx);
318 if let Some(app_state) = app_state.upgrade() {
319 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
320 Editor::new_file(workspace, &Default::default(), cx)
321 })
322 .detach();
323 }
324 });
325 cx.on_action(move |_: &workspace::NewWindow, cx| {
326 let app_state = workspace::AppState::global(cx);
327 if let Some(app_state) = app_state.upgrade() {
328 workspace::open_new(Default::default(), app_state, cx, |workspace, cx| {
329 Editor::new_file(workspace, &Default::default(), cx)
330 })
331 .detach();
332 }
333 });
334 git::project_diff::init(cx);
335}
336
337pub struct SearchWithinRange;
338
339trait InvalidationRegion {
340 fn ranges(&self) -> &[Range<Anchor>];
341}
342
343#[derive(Clone, Debug, PartialEq)]
344pub enum SelectPhase {
345 Begin {
346 position: DisplayPoint,
347 add: bool,
348 click_count: usize,
349 },
350 BeginColumnar {
351 position: DisplayPoint,
352 reset: bool,
353 goal_column: u32,
354 },
355 Extend {
356 position: DisplayPoint,
357 click_count: usize,
358 },
359 Update {
360 position: DisplayPoint,
361 goal_column: u32,
362 scroll_delta: gpui::Point<f32>,
363 },
364 End,
365}
366
367#[derive(Clone, Debug)]
368pub enum SelectMode {
369 Character,
370 Word(Range<Anchor>),
371 Line(Range<Anchor>),
372 All,
373}
374
375#[derive(Copy, Clone, PartialEq, Eq, Debug)]
376pub enum EditorMode {
377 SingleLine { auto_width: bool },
378 AutoHeight { max_lines: usize },
379 Full,
380}
381
382#[derive(Copy, Clone, Debug)]
383pub enum SoftWrap {
384 /// Prefer not to wrap at all.
385 ///
386 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
387 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
388 GitDiff,
389 /// Prefer a single line generally, unless an overly long line is encountered.
390 None,
391 /// Soft wrap lines that exceed the editor width.
392 EditorWidth,
393 /// Soft wrap lines at the preferred line length.
394 Column(u32),
395 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
396 Bounded(u32),
397}
398
399#[derive(Clone)]
400pub struct EditorStyle {
401 pub background: Hsla,
402 pub local_player: PlayerColor,
403 pub text: TextStyle,
404 pub scrollbar_width: Pixels,
405 pub syntax: Arc<SyntaxTheme>,
406 pub status: StatusColors,
407 pub inlay_hints_style: HighlightStyle,
408 pub inline_completion_styles: InlineCompletionStyles,
409 pub unnecessary_code_fade: f32,
410}
411
412impl Default for EditorStyle {
413 fn default() -> Self {
414 Self {
415 background: Hsla::default(),
416 local_player: PlayerColor::default(),
417 text: TextStyle::default(),
418 scrollbar_width: Pixels::default(),
419 syntax: Default::default(),
420 // HACK: Status colors don't have a real default.
421 // We should look into removing the status colors from the editor
422 // style and retrieve them directly from the theme.
423 status: StatusColors::dark(),
424 inlay_hints_style: HighlightStyle::default(),
425 inline_completion_styles: InlineCompletionStyles {
426 insertion: HighlightStyle::default(),
427 whitespace: HighlightStyle::default(),
428 },
429 unnecessary_code_fade: Default::default(),
430 }
431 }
432}
433
434pub fn make_inlay_hints_style(cx: &WindowContext) -> HighlightStyle {
435 let show_background = language_settings::language_settings(None, None, cx)
436 .inlay_hints
437 .show_background;
438
439 HighlightStyle {
440 color: Some(cx.theme().status().hint),
441 background_color: show_background.then(|| cx.theme().status().hint_background),
442 ..HighlightStyle::default()
443 }
444}
445
446pub fn make_suggestion_styles(cx: &WindowContext) -> InlineCompletionStyles {
447 InlineCompletionStyles {
448 insertion: HighlightStyle {
449 color: Some(cx.theme().status().predictive),
450 ..HighlightStyle::default()
451 },
452 whitespace: HighlightStyle {
453 background_color: Some(cx.theme().status().created_background),
454 ..HighlightStyle::default()
455 },
456 }
457}
458
459type CompletionId = usize;
460
461#[derive(Debug, Clone)]
462enum InlineCompletionMenuHint {
463 Loading,
464 Loaded { text: InlineCompletionText },
465 None,
466}
467
468impl InlineCompletionMenuHint {
469 pub fn label(&self) -> &'static str {
470 match self {
471 InlineCompletionMenuHint::Loading | InlineCompletionMenuHint::Loaded { .. } => {
472 "Edit Prediction"
473 }
474 InlineCompletionMenuHint::None => "No Prediction",
475 }
476 }
477}
478
479#[derive(Clone, Debug)]
480enum InlineCompletionText {
481 Move(SharedString),
482 Edit {
483 text: SharedString,
484 highlights: Vec<(Range<usize>, HighlightStyle)>,
485 },
486}
487
488enum InlineCompletion {
489 Edit(Vec<(Range<Anchor>, String)>),
490 Move(Anchor),
491}
492
493struct InlineCompletionState {
494 inlay_ids: Vec<InlayId>,
495 completion: InlineCompletion,
496 invalidation_range: Range<Anchor>,
497}
498
499enum InlineCompletionHighlight {}
500
501#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
502struct EditorActionId(usize);
503
504impl EditorActionId {
505 pub fn post_inc(&mut self) -> Self {
506 let answer = self.0;
507
508 *self = Self(answer + 1);
509
510 Self(answer)
511 }
512}
513
514// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
515// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
516
517type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
518type GutterHighlight = (fn(&AppContext) -> Hsla, Arc<[Range<Anchor>]>);
519
520#[derive(Default)]
521struct ScrollbarMarkerState {
522 scrollbar_size: Size<Pixels>,
523 dirty: bool,
524 markers: Arc<[PaintQuad]>,
525 pending_refresh: Option<Task<Result<()>>>,
526}
527
528impl ScrollbarMarkerState {
529 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
530 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
531 }
532}
533
534#[derive(Clone, Debug)]
535struct RunnableTasks {
536 templates: Vec<(TaskSourceKind, TaskTemplate)>,
537 offset: MultiBufferOffset,
538 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
539 column: u32,
540 // Values of all named captures, including those starting with '_'
541 extra_variables: HashMap<String, String>,
542 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
543 context_range: Range<BufferOffset>,
544}
545
546impl RunnableTasks {
547 fn resolve<'a>(
548 &'a self,
549 cx: &'a task::TaskContext,
550 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
551 self.templates.iter().filter_map(|(kind, template)| {
552 template
553 .resolve_task(&kind.to_id_base(), cx)
554 .map(|task| (kind.clone(), task))
555 })
556 }
557}
558
559#[derive(Clone)]
560struct ResolvedTasks {
561 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
562 position: Anchor,
563}
564#[derive(Copy, Clone, Debug)]
565struct MultiBufferOffset(usize);
566#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
567struct BufferOffset(usize);
568
569// Addons allow storing per-editor state in other crates (e.g. Vim)
570pub trait Addon: 'static {
571 fn extend_key_context(&self, _: &mut KeyContext, _: &AppContext) {}
572
573 fn to_any(&self) -> &dyn std::any::Any;
574}
575
576#[derive(Debug, Copy, Clone, PartialEq, Eq)]
577pub enum IsVimMode {
578 Yes,
579 No,
580}
581
582/// Zed's primary text input `View`, allowing users to edit a [`MultiBuffer`]
583///
584/// See the [module level documentation](self) for more information.
585pub struct Editor {
586 focus_handle: FocusHandle,
587 last_focused_descendant: Option<WeakFocusHandle>,
588 /// The text buffer being edited
589 buffer: Model<MultiBuffer>,
590 /// Map of how text in the buffer should be displayed.
591 /// Handles soft wraps, folds, fake inlay text insertions, etc.
592 pub display_map: Model<DisplayMap>,
593 pub selections: SelectionsCollection,
594 pub scroll_manager: ScrollManager,
595 /// When inline assist editors are linked, they all render cursors because
596 /// typing enters text into each of them, even the ones that aren't focused.
597 pub(crate) show_cursor_when_unfocused: bool,
598 columnar_selection_tail: Option<Anchor>,
599 add_selections_state: Option<AddSelectionsState>,
600 select_next_state: Option<SelectNextState>,
601 select_prev_state: Option<SelectNextState>,
602 selection_history: SelectionHistory,
603 autoclose_regions: Vec<AutocloseRegion>,
604 snippet_stack: InvalidationStack<SnippetState>,
605 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
606 ime_transaction: Option<TransactionId>,
607 active_diagnostics: Option<ActiveDiagnosticGroup>,
608 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
609
610 project: Option<Model<Project>>,
611 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
612 completion_provider: Option<Box<dyn CompletionProvider>>,
613 collaboration_hub: Option<Box<dyn CollaborationHub>>,
614 blink_manager: Model<BlinkManager>,
615 show_cursor_names: bool,
616 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
617 pub show_local_selections: bool,
618 mode: EditorMode,
619 show_breadcrumbs: bool,
620 show_gutter: bool,
621 show_scrollbars: bool,
622 show_line_numbers: Option<bool>,
623 use_relative_line_numbers: Option<bool>,
624 show_git_diff_gutter: Option<bool>,
625 show_code_actions: Option<bool>,
626 show_runnables: Option<bool>,
627 show_wrap_guides: Option<bool>,
628 show_indent_guides: Option<bool>,
629 placeholder_text: Option<Arc<str>>,
630 highlight_order: usize,
631 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
632 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
633 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
634 scrollbar_marker_state: ScrollbarMarkerState,
635 active_indent_guides_state: ActiveIndentGuidesState,
636 nav_history: Option<ItemNavHistory>,
637 context_menu: RefCell<Option<CodeContextMenu>>,
638 mouse_context_menu: Option<MouseContextMenu>,
639 hunk_controls_menu_handle: PopoverMenuHandle<ui::ContextMenu>,
640 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
641 signature_help_state: SignatureHelpState,
642 auto_signature_help: Option<bool>,
643 find_all_references_task_sources: Vec<Anchor>,
644 next_completion_id: CompletionId,
645 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
646 code_actions_task: Option<Task<Result<()>>>,
647 document_highlights_task: Option<Task<()>>,
648 linked_editing_range_task: Option<Task<Option<()>>>,
649 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
650 pending_rename: Option<RenameState>,
651 searchable: bool,
652 cursor_shape: CursorShape,
653 current_line_highlight: Option<CurrentLineHighlight>,
654 collapse_matches: bool,
655 autoindent_mode: Option<AutoindentMode>,
656 workspace: Option<(WeakView<Workspace>, Option<WorkspaceId>)>,
657 input_enabled: bool,
658 use_modal_editing: bool,
659 read_only: bool,
660 leader_peer_id: Option<PeerId>,
661 remote_id: Option<ViewId>,
662 hover_state: HoverState,
663 gutter_hovered: bool,
664 hovered_link_state: Option<HoveredLinkState>,
665 inline_completion_provider: Option<RegisteredInlineCompletionProvider>,
666 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
667 active_inline_completion: Option<InlineCompletionState>,
668 // enable_inline_completions is a switch that Vim can use to disable
669 // inline completions based on its mode.
670 enable_inline_completions: bool,
671 show_inline_completions_override: Option<bool>,
672 inlay_hint_cache: InlayHintCache,
673 diff_map: DiffMap,
674 next_inlay_id: usize,
675 _subscriptions: Vec<Subscription>,
676 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
677 gutter_dimensions: GutterDimensions,
678 style: Option<EditorStyle>,
679 text_style_refinement: Option<TextStyleRefinement>,
680 next_editor_action_id: EditorActionId,
681 editor_actions: Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut ViewContext<Self>)>>>>,
682 use_autoclose: bool,
683 use_auto_surround: bool,
684 auto_replace_emoji_shortcode: bool,
685 show_git_blame_gutter: bool,
686 show_git_blame_inline: bool,
687 show_git_blame_inline_delay_task: Option<Task<()>>,
688 git_blame_inline_enabled: bool,
689 serialize_dirty_buffers: bool,
690 show_selection_menu: Option<bool>,
691 blame: Option<Model<GitBlame>>,
692 blame_subscription: Option<Subscription>,
693 custom_context_menu: Option<
694 Box<
695 dyn 'static
696 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
697 >,
698 >,
699 last_bounds: Option<Bounds<Pixels>>,
700 expect_bounds_change: Option<Bounds<Pixels>>,
701 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
702 tasks_update_task: Option<Task<()>>,
703 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
704 breadcrumb_header: Option<String>,
705 focused_block: Option<FocusedBlock>,
706 next_scroll_position: NextScrollCursorCenterTopBottom,
707 addons: HashMap<TypeId, Box<dyn Addon>>,
708 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
709 toggle_fold_multiple_buffers: Task<()>,
710 _scroll_cursor_center_top_bottom_task: Task<()>,
711}
712
713#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
714enum NextScrollCursorCenterTopBottom {
715 #[default]
716 Center,
717 Top,
718 Bottom,
719}
720
721impl NextScrollCursorCenterTopBottom {
722 fn next(&self) -> Self {
723 match self {
724 Self::Center => Self::Top,
725 Self::Top => Self::Bottom,
726 Self::Bottom => Self::Center,
727 }
728 }
729}
730
731#[derive(Clone)]
732pub struct EditorSnapshot {
733 pub mode: EditorMode,
734 show_gutter: bool,
735 show_line_numbers: Option<bool>,
736 show_git_diff_gutter: Option<bool>,
737 show_code_actions: Option<bool>,
738 show_runnables: Option<bool>,
739 git_blame_gutter_max_author_length: Option<usize>,
740 pub display_snapshot: DisplaySnapshot,
741 pub placeholder_text: Option<Arc<str>>,
742 diff_map: DiffMapSnapshot,
743 is_focused: bool,
744 scroll_anchor: ScrollAnchor,
745 ongoing_scroll: OngoingScroll,
746 current_line_highlight: CurrentLineHighlight,
747 gutter_hovered: bool,
748}
749
750const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
751
752#[derive(Default, Debug, Clone, Copy)]
753pub struct GutterDimensions {
754 pub left_padding: Pixels,
755 pub right_padding: Pixels,
756 pub width: Pixels,
757 pub margin: Pixels,
758 pub git_blame_entries_width: Option<Pixels>,
759}
760
761impl GutterDimensions {
762 /// The full width of the space taken up by the gutter.
763 pub fn full_width(&self) -> Pixels {
764 self.margin + self.width
765 }
766
767 /// The width of the space reserved for the fold indicators,
768 /// use alongside 'justify_end' and `gutter_width` to
769 /// right align content with the line numbers
770 pub fn fold_area_width(&self) -> Pixels {
771 self.margin + self.right_padding
772 }
773}
774
775#[derive(Debug)]
776pub struct RemoteSelection {
777 pub replica_id: ReplicaId,
778 pub selection: Selection<Anchor>,
779 pub cursor_shape: CursorShape,
780 pub peer_id: PeerId,
781 pub line_mode: bool,
782 pub participant_index: Option<ParticipantIndex>,
783 pub user_name: Option<SharedString>,
784}
785
786#[derive(Clone, Debug)]
787struct SelectionHistoryEntry {
788 selections: Arc<[Selection<Anchor>]>,
789 select_next_state: Option<SelectNextState>,
790 select_prev_state: Option<SelectNextState>,
791 add_selections_state: Option<AddSelectionsState>,
792}
793
794enum SelectionHistoryMode {
795 Normal,
796 Undoing,
797 Redoing,
798}
799
800#[derive(Clone, PartialEq, Eq, Hash)]
801struct HoveredCursor {
802 replica_id: u16,
803 selection_id: usize,
804}
805
806impl Default for SelectionHistoryMode {
807 fn default() -> Self {
808 Self::Normal
809 }
810}
811
812#[derive(Default)]
813struct SelectionHistory {
814 #[allow(clippy::type_complexity)]
815 selections_by_transaction:
816 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
817 mode: SelectionHistoryMode,
818 undo_stack: VecDeque<SelectionHistoryEntry>,
819 redo_stack: VecDeque<SelectionHistoryEntry>,
820}
821
822impl SelectionHistory {
823 fn insert_transaction(
824 &mut self,
825 transaction_id: TransactionId,
826 selections: Arc<[Selection<Anchor>]>,
827 ) {
828 self.selections_by_transaction
829 .insert(transaction_id, (selections, None));
830 }
831
832 #[allow(clippy::type_complexity)]
833 fn transaction(
834 &self,
835 transaction_id: TransactionId,
836 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
837 self.selections_by_transaction.get(&transaction_id)
838 }
839
840 #[allow(clippy::type_complexity)]
841 fn transaction_mut(
842 &mut self,
843 transaction_id: TransactionId,
844 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
845 self.selections_by_transaction.get_mut(&transaction_id)
846 }
847
848 fn push(&mut self, entry: SelectionHistoryEntry) {
849 if !entry.selections.is_empty() {
850 match self.mode {
851 SelectionHistoryMode::Normal => {
852 self.push_undo(entry);
853 self.redo_stack.clear();
854 }
855 SelectionHistoryMode::Undoing => self.push_redo(entry),
856 SelectionHistoryMode::Redoing => self.push_undo(entry),
857 }
858 }
859 }
860
861 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
862 if self
863 .undo_stack
864 .back()
865 .map_or(true, |e| e.selections != entry.selections)
866 {
867 self.undo_stack.push_back(entry);
868 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
869 self.undo_stack.pop_front();
870 }
871 }
872 }
873
874 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
875 if self
876 .redo_stack
877 .back()
878 .map_or(true, |e| e.selections != entry.selections)
879 {
880 self.redo_stack.push_back(entry);
881 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
882 self.redo_stack.pop_front();
883 }
884 }
885 }
886}
887
888struct RowHighlight {
889 index: usize,
890 range: Range<Anchor>,
891 color: Hsla,
892 should_autoscroll: bool,
893}
894
895#[derive(Clone, Debug)]
896struct AddSelectionsState {
897 above: bool,
898 stack: Vec<usize>,
899}
900
901#[derive(Clone)]
902struct SelectNextState {
903 query: AhoCorasick,
904 wordwise: bool,
905 done: bool,
906}
907
908impl std::fmt::Debug for SelectNextState {
909 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
910 f.debug_struct(std::any::type_name::<Self>())
911 .field("wordwise", &self.wordwise)
912 .field("done", &self.done)
913 .finish()
914 }
915}
916
917#[derive(Debug)]
918struct AutocloseRegion {
919 selection_id: usize,
920 range: Range<Anchor>,
921 pair: BracketPair,
922}
923
924#[derive(Debug)]
925struct SnippetState {
926 ranges: Vec<Vec<Range<Anchor>>>,
927 active_index: usize,
928 choices: Vec<Option<Vec<String>>>,
929}
930
931#[doc(hidden)]
932pub struct RenameState {
933 pub range: Range<Anchor>,
934 pub old_name: Arc<str>,
935 pub editor: View<Editor>,
936 block_id: CustomBlockId,
937}
938
939struct InvalidationStack<T>(Vec<T>);
940
941struct RegisteredInlineCompletionProvider {
942 provider: Arc<dyn InlineCompletionProviderHandle>,
943 _subscription: Subscription,
944}
945
946#[derive(Debug)]
947struct ActiveDiagnosticGroup {
948 primary_range: Range<Anchor>,
949 primary_message: String,
950 group_id: usize,
951 blocks: HashMap<CustomBlockId, Diagnostic>,
952 is_valid: bool,
953}
954
955#[derive(Serialize, Deserialize, Clone, Debug)]
956pub struct ClipboardSelection {
957 pub len: usize,
958 pub is_entire_line: bool,
959 pub first_line_indent: u32,
960}
961
962#[derive(Debug)]
963pub(crate) struct NavigationData {
964 cursor_anchor: Anchor,
965 cursor_position: Point,
966 scroll_anchor: ScrollAnchor,
967 scroll_top_row: u32,
968}
969
970#[derive(Debug, Clone, Copy, PartialEq, Eq)]
971pub enum GotoDefinitionKind {
972 Symbol,
973 Declaration,
974 Type,
975 Implementation,
976}
977
978#[derive(Debug, Clone)]
979enum InlayHintRefreshReason {
980 Toggle(bool),
981 SettingsChange(InlayHintSettings),
982 NewLinesShown,
983 BufferEdited(HashSet<Arc<Language>>),
984 RefreshRequested,
985 ExcerptsRemoved(Vec<ExcerptId>),
986}
987
988impl InlayHintRefreshReason {
989 fn description(&self) -> &'static str {
990 match self {
991 Self::Toggle(_) => "toggle",
992 Self::SettingsChange(_) => "settings change",
993 Self::NewLinesShown => "new lines shown",
994 Self::BufferEdited(_) => "buffer edited",
995 Self::RefreshRequested => "refresh requested",
996 Self::ExcerptsRemoved(_) => "excerpts removed",
997 }
998 }
999}
1000
1001pub(crate) struct FocusedBlock {
1002 id: BlockId,
1003 focus_handle: WeakFocusHandle,
1004}
1005
1006#[derive(Clone)]
1007enum JumpData {
1008 MultiBufferRow {
1009 row: MultiBufferRow,
1010 line_offset_from_top: u32,
1011 },
1012 MultiBufferPoint {
1013 excerpt_id: ExcerptId,
1014 position: Point,
1015 anchor: text::Anchor,
1016 line_offset_from_top: u32,
1017 },
1018}
1019
1020impl Editor {
1021 pub fn single_line(cx: &mut ViewContext<Self>) -> Self {
1022 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1023 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1024 Self::new(
1025 EditorMode::SingleLine { auto_width: false },
1026 buffer,
1027 None,
1028 false,
1029 cx,
1030 )
1031 }
1032
1033 pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
1034 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1035 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1036 Self::new(EditorMode::Full, buffer, None, false, cx)
1037 }
1038
1039 pub fn auto_width(cx: &mut ViewContext<Self>) -> Self {
1040 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1041 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1042 Self::new(
1043 EditorMode::SingleLine { auto_width: true },
1044 buffer,
1045 None,
1046 false,
1047 cx,
1048 )
1049 }
1050
1051 pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
1052 let buffer = cx.new_model(|cx| Buffer::local("", cx));
1053 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1054 Self::new(
1055 EditorMode::AutoHeight { max_lines },
1056 buffer,
1057 None,
1058 false,
1059 cx,
1060 )
1061 }
1062
1063 pub fn for_buffer(
1064 buffer: Model<Buffer>,
1065 project: Option<Model<Project>>,
1066 cx: &mut ViewContext<Self>,
1067 ) -> Self {
1068 let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
1069 Self::new(EditorMode::Full, buffer, project, false, cx)
1070 }
1071
1072 pub fn for_multibuffer(
1073 buffer: Model<MultiBuffer>,
1074 project: Option<Model<Project>>,
1075 show_excerpt_controls: bool,
1076 cx: &mut ViewContext<Self>,
1077 ) -> Self {
1078 Self::new(EditorMode::Full, buffer, project, show_excerpt_controls, cx)
1079 }
1080
1081 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1082 let show_excerpt_controls = self.display_map.read(cx).show_excerpt_controls();
1083 let mut clone = Self::new(
1084 self.mode,
1085 self.buffer.clone(),
1086 self.project.clone(),
1087 show_excerpt_controls,
1088 cx,
1089 );
1090 self.display_map.update(cx, |display_map, cx| {
1091 let snapshot = display_map.snapshot(cx);
1092 clone.display_map.update(cx, |display_map, cx| {
1093 display_map.set_state(&snapshot, cx);
1094 });
1095 });
1096 clone.selections.clone_state(&self.selections);
1097 clone.scroll_manager.clone_state(&self.scroll_manager);
1098 clone.searchable = self.searchable;
1099 clone
1100 }
1101
1102 pub fn new(
1103 mode: EditorMode,
1104 buffer: Model<MultiBuffer>,
1105 project: Option<Model<Project>>,
1106 show_excerpt_controls: bool,
1107 cx: &mut ViewContext<Self>,
1108 ) -> Self {
1109 let style = cx.text_style();
1110 let font_size = style.font_size.to_pixels(cx.rem_size());
1111 let editor = cx.view().downgrade();
1112 let fold_placeholder = FoldPlaceholder {
1113 constrain_width: true,
1114 render: Arc::new(move |fold_id, fold_range, cx| {
1115 let editor = editor.clone();
1116 div()
1117 .id(fold_id)
1118 .bg(cx.theme().colors().ghost_element_background)
1119 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1120 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1121 .rounded_sm()
1122 .size_full()
1123 .cursor_pointer()
1124 .child("⋯")
1125 .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation())
1126 .on_click(move |_, cx| {
1127 editor
1128 .update(cx, |editor, cx| {
1129 editor.unfold_ranges(
1130 &[fold_range.start..fold_range.end],
1131 true,
1132 false,
1133 cx,
1134 );
1135 cx.stop_propagation();
1136 })
1137 .ok();
1138 })
1139 .into_any()
1140 }),
1141 merge_adjacent: true,
1142 ..Default::default()
1143 };
1144 let display_map = cx.new_model(|cx| {
1145 DisplayMap::new(
1146 buffer.clone(),
1147 style.font(),
1148 font_size,
1149 None,
1150 show_excerpt_controls,
1151 FILE_HEADER_HEIGHT,
1152 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1153 MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
1154 fold_placeholder,
1155 cx,
1156 )
1157 });
1158
1159 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1160
1161 let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1162
1163 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1164 .then(|| language_settings::SoftWrap::None);
1165
1166 let mut project_subscriptions = Vec::new();
1167 if mode == EditorMode::Full {
1168 if let Some(project) = project.as_ref() {
1169 if buffer.read(cx).is_singleton() {
1170 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1171 cx.emit(EditorEvent::TitleChanged);
1172 }));
1173 }
1174 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1175 if let project::Event::RefreshInlayHints = event {
1176 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1177 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1178 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1179 let focus_handle = editor.focus_handle(cx);
1180 if focus_handle.is_focused(cx) {
1181 let snapshot = buffer.read(cx).snapshot();
1182 for (range, snippet) in snippet_edits {
1183 let editor_range =
1184 language::range_from_lsp(*range).to_offset(&snapshot);
1185 editor
1186 .insert_snippet(&[editor_range], snippet.clone(), cx)
1187 .ok();
1188 }
1189 }
1190 }
1191 }
1192 }));
1193 if let Some(task_inventory) = project
1194 .read(cx)
1195 .task_store()
1196 .read(cx)
1197 .task_inventory()
1198 .cloned()
1199 {
1200 project_subscriptions.push(cx.observe(&task_inventory, |editor, _, cx| {
1201 editor.tasks_update_task = Some(editor.refresh_runnables(cx));
1202 }));
1203 }
1204 }
1205 }
1206
1207 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1208
1209 let inlay_hint_settings =
1210 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1211 let focus_handle = cx.focus_handle();
1212 cx.on_focus(&focus_handle, Self::handle_focus).detach();
1213 cx.on_focus_in(&focus_handle, Self::handle_focus_in)
1214 .detach();
1215 cx.on_focus_out(&focus_handle, Self::handle_focus_out)
1216 .detach();
1217 cx.on_blur(&focus_handle, Self::handle_blur).detach();
1218
1219 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1220 Some(false)
1221 } else {
1222 None
1223 };
1224
1225 let mut code_action_providers = Vec::new();
1226 if let Some(project) = project.clone() {
1227 get_unstaged_changes_for_buffers(&project, buffer.read(cx).all_buffers(), cx);
1228 code_action_providers.push(Rc::new(project) as Rc<_>);
1229 }
1230
1231 let mut this = Self {
1232 focus_handle,
1233 show_cursor_when_unfocused: false,
1234 last_focused_descendant: None,
1235 buffer: buffer.clone(),
1236 display_map: display_map.clone(),
1237 selections,
1238 scroll_manager: ScrollManager::new(cx),
1239 columnar_selection_tail: None,
1240 add_selections_state: None,
1241 select_next_state: None,
1242 select_prev_state: None,
1243 selection_history: Default::default(),
1244 autoclose_regions: Default::default(),
1245 snippet_stack: Default::default(),
1246 select_larger_syntax_node_stack: Vec::new(),
1247 ime_transaction: Default::default(),
1248 active_diagnostics: None,
1249 soft_wrap_mode_override,
1250 completion_provider: project.clone().map(|project| Box::new(project) as _),
1251 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1252 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1253 project,
1254 blink_manager: blink_manager.clone(),
1255 show_local_selections: true,
1256 show_scrollbars: true,
1257 mode,
1258 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1259 show_gutter: mode == EditorMode::Full,
1260 show_line_numbers: None,
1261 use_relative_line_numbers: None,
1262 show_git_diff_gutter: None,
1263 show_code_actions: None,
1264 show_runnables: None,
1265 show_wrap_guides: None,
1266 show_indent_guides,
1267 placeholder_text: None,
1268 highlight_order: 0,
1269 highlighted_rows: HashMap::default(),
1270 background_highlights: Default::default(),
1271 gutter_highlights: TreeMap::default(),
1272 scrollbar_marker_state: ScrollbarMarkerState::default(),
1273 active_indent_guides_state: ActiveIndentGuidesState::default(),
1274 nav_history: None,
1275 context_menu: RefCell::new(None),
1276 mouse_context_menu: None,
1277 hunk_controls_menu_handle: PopoverMenuHandle::default(),
1278 completion_tasks: Default::default(),
1279 signature_help_state: SignatureHelpState::default(),
1280 auto_signature_help: None,
1281 find_all_references_task_sources: Vec::new(),
1282 next_completion_id: 0,
1283 next_inlay_id: 0,
1284 code_action_providers,
1285 available_code_actions: Default::default(),
1286 code_actions_task: Default::default(),
1287 document_highlights_task: Default::default(),
1288 linked_editing_range_task: Default::default(),
1289 pending_rename: Default::default(),
1290 searchable: true,
1291 cursor_shape: EditorSettings::get_global(cx)
1292 .cursor_shape
1293 .unwrap_or_default(),
1294 current_line_highlight: None,
1295 autoindent_mode: Some(AutoindentMode::EachLine),
1296 collapse_matches: false,
1297 workspace: None,
1298 input_enabled: true,
1299 use_modal_editing: mode == EditorMode::Full,
1300 read_only: false,
1301 use_autoclose: true,
1302 use_auto_surround: true,
1303 auto_replace_emoji_shortcode: false,
1304 leader_peer_id: None,
1305 remote_id: None,
1306 hover_state: Default::default(),
1307 hovered_link_state: Default::default(),
1308 inline_completion_provider: None,
1309 active_inline_completion: None,
1310 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1311 diff_map: DiffMap::default(),
1312 gutter_hovered: false,
1313 pixel_position_of_newest_cursor: None,
1314 last_bounds: None,
1315 expect_bounds_change: None,
1316 gutter_dimensions: GutterDimensions::default(),
1317 style: None,
1318 show_cursor_names: false,
1319 hovered_cursors: Default::default(),
1320 next_editor_action_id: EditorActionId::default(),
1321 editor_actions: Rc::default(),
1322 show_inline_completions_override: None,
1323 enable_inline_completions: true,
1324 custom_context_menu: None,
1325 show_git_blame_gutter: false,
1326 show_git_blame_inline: false,
1327 show_selection_menu: None,
1328 show_git_blame_inline_delay_task: None,
1329 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1330 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1331 .session
1332 .restore_unsaved_buffers,
1333 blame: None,
1334 blame_subscription: None,
1335 tasks: Default::default(),
1336 _subscriptions: vec![
1337 cx.observe(&buffer, Self::on_buffer_changed),
1338 cx.subscribe(&buffer, Self::on_buffer_event),
1339 cx.observe(&display_map, Self::on_display_map_changed),
1340 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1341 cx.observe_global::<SettingsStore>(Self::settings_changed),
1342 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1343 cx.observe_window_activation(|editor, cx| {
1344 let active = cx.is_window_active();
1345 editor.blink_manager.update(cx, |blink_manager, cx| {
1346 if active {
1347 blink_manager.enable(cx);
1348 } else {
1349 blink_manager.disable(cx);
1350 }
1351 });
1352 }),
1353 ],
1354 tasks_update_task: None,
1355 linked_edit_ranges: Default::default(),
1356 previous_search_ranges: None,
1357 breadcrumb_header: None,
1358 focused_block: None,
1359 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1360 addons: HashMap::default(),
1361 registered_buffers: HashMap::default(),
1362 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1363 toggle_fold_multiple_buffers: Task::ready(()),
1364 text_style_refinement: None,
1365 };
1366 this.tasks_update_task = Some(this.refresh_runnables(cx));
1367 this._subscriptions.extend(project_subscriptions);
1368
1369 this.end_selection(cx);
1370 this.scroll_manager.show_scrollbar(cx);
1371
1372 if mode == EditorMode::Full {
1373 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1374 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1375
1376 if this.git_blame_inline_enabled {
1377 this.git_blame_inline_enabled = true;
1378 this.start_git_blame_inline(false, cx);
1379 }
1380
1381 if let Some(buffer) = buffer.read(cx).as_singleton() {
1382 if let Some(project) = this.project.as_ref() {
1383 let lsp_store = project.read(cx).lsp_store();
1384 let handle = lsp_store.update(cx, |lsp_store, cx| {
1385 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1386 });
1387 this.registered_buffers
1388 .insert(buffer.read(cx).remote_id(), handle);
1389 }
1390 }
1391 }
1392
1393 this.report_editor_event("Editor Opened", None, cx);
1394 this
1395 }
1396
1397 pub fn mouse_menu_is_focused(&self, cx: &WindowContext) -> bool {
1398 self.mouse_context_menu
1399 .as_ref()
1400 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(cx))
1401 }
1402
1403 fn key_context(&self, cx: &ViewContext<Self>) -> KeyContext {
1404 let mut key_context = KeyContext::new_with_defaults();
1405 key_context.add("Editor");
1406 let mode = match self.mode {
1407 EditorMode::SingleLine { .. } => "single_line",
1408 EditorMode::AutoHeight { .. } => "auto_height",
1409 EditorMode::Full => "full",
1410 };
1411
1412 if EditorSettings::jupyter_enabled(cx) {
1413 key_context.add("jupyter");
1414 }
1415
1416 key_context.set("mode", mode);
1417 if self.pending_rename.is_some() {
1418 key_context.add("renaming");
1419 }
1420 match self.context_menu.borrow().as_ref() {
1421 Some(CodeContextMenu::Completions(_)) => {
1422 key_context.add("menu");
1423 key_context.add("showing_completions")
1424 }
1425 Some(CodeContextMenu::CodeActions(_)) => {
1426 key_context.add("menu");
1427 key_context.add("showing_code_actions")
1428 }
1429 None => {}
1430 }
1431
1432 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1433 if !self.focus_handle(cx).contains_focused(cx)
1434 || (self.is_focused(cx) || self.mouse_menu_is_focused(cx))
1435 {
1436 for addon in self.addons.values() {
1437 addon.extend_key_context(&mut key_context, cx)
1438 }
1439 }
1440
1441 if let Some(extension) = self
1442 .buffer
1443 .read(cx)
1444 .as_singleton()
1445 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
1446 {
1447 key_context.set("extension", extension.to_string());
1448 }
1449
1450 if self.has_active_inline_completion() {
1451 key_context.add("copilot_suggestion");
1452 key_context.add("inline_completion");
1453 }
1454
1455 if !self
1456 .selections
1457 .disjoint
1458 .iter()
1459 .all(|selection| selection.start == selection.end)
1460 {
1461 key_context.add("selection");
1462 }
1463
1464 key_context
1465 }
1466
1467 pub fn new_file(
1468 workspace: &mut Workspace,
1469 _: &workspace::NewFile,
1470 cx: &mut ViewContext<Workspace>,
1471 ) {
1472 Self::new_in_workspace(workspace, cx).detach_and_prompt_err(
1473 "Failed to create buffer",
1474 cx,
1475 |e, _| match e.error_code() {
1476 ErrorCode::RemoteUpgradeRequired => Some(format!(
1477 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1478 e.error_tag("required").unwrap_or("the latest version")
1479 )),
1480 _ => None,
1481 },
1482 );
1483 }
1484
1485 pub fn new_in_workspace(
1486 workspace: &mut Workspace,
1487 cx: &mut ViewContext<Workspace>,
1488 ) -> Task<Result<View<Editor>>> {
1489 let project = workspace.project().clone();
1490 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1491
1492 cx.spawn(|workspace, mut cx| async move {
1493 let buffer = create.await?;
1494 workspace.update(&mut cx, |workspace, cx| {
1495 let editor =
1496 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx));
1497 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
1498 editor
1499 })
1500 })
1501 }
1502
1503 fn new_file_vertical(
1504 workspace: &mut Workspace,
1505 _: &workspace::NewFileSplitVertical,
1506 cx: &mut ViewContext<Workspace>,
1507 ) {
1508 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), cx)
1509 }
1510
1511 fn new_file_horizontal(
1512 workspace: &mut Workspace,
1513 _: &workspace::NewFileSplitHorizontal,
1514 cx: &mut ViewContext<Workspace>,
1515 ) {
1516 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), cx)
1517 }
1518
1519 fn new_file_in_direction(
1520 workspace: &mut Workspace,
1521 direction: SplitDirection,
1522 cx: &mut ViewContext<Workspace>,
1523 ) {
1524 let project = workspace.project().clone();
1525 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1526
1527 cx.spawn(|workspace, mut cx| async move {
1528 let buffer = create.await?;
1529 workspace.update(&mut cx, move |workspace, cx| {
1530 workspace.split_item(
1531 direction,
1532 Box::new(
1533 cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
1534 ),
1535 cx,
1536 )
1537 })?;
1538 anyhow::Ok(())
1539 })
1540 .detach_and_prompt_err("Failed to create buffer", cx, |e, _| match e.error_code() {
1541 ErrorCode::RemoteUpgradeRequired => Some(format!(
1542 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1543 e.error_tag("required").unwrap_or("the latest version")
1544 )),
1545 _ => None,
1546 });
1547 }
1548
1549 pub fn leader_peer_id(&self) -> Option<PeerId> {
1550 self.leader_peer_id
1551 }
1552
1553 pub fn buffer(&self) -> &Model<MultiBuffer> {
1554 &self.buffer
1555 }
1556
1557 pub fn workspace(&self) -> Option<View<Workspace>> {
1558 self.workspace.as_ref()?.0.upgrade()
1559 }
1560
1561 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1562 self.buffer().read(cx).title(cx)
1563 }
1564
1565 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1566 let git_blame_gutter_max_author_length = self
1567 .render_git_blame_gutter(cx)
1568 .then(|| {
1569 if let Some(blame) = self.blame.as_ref() {
1570 let max_author_length =
1571 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1572 Some(max_author_length)
1573 } else {
1574 None
1575 }
1576 })
1577 .flatten();
1578
1579 EditorSnapshot {
1580 mode: self.mode,
1581 show_gutter: self.show_gutter,
1582 show_line_numbers: self.show_line_numbers,
1583 show_git_diff_gutter: self.show_git_diff_gutter,
1584 show_code_actions: self.show_code_actions,
1585 show_runnables: self.show_runnables,
1586 git_blame_gutter_max_author_length,
1587 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1588 scroll_anchor: self.scroll_manager.anchor(),
1589 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1590 placeholder_text: self.placeholder_text.clone(),
1591 diff_map: self.diff_map.snapshot(),
1592 is_focused: self.focus_handle.is_focused(cx),
1593 current_line_highlight: self
1594 .current_line_highlight
1595 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1596 gutter_hovered: self.gutter_hovered,
1597 }
1598 }
1599
1600 pub fn language_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> Option<Arc<Language>> {
1601 self.buffer.read(cx).language_at(point, cx)
1602 }
1603
1604 pub fn file_at<T: ToOffset>(
1605 &self,
1606 point: T,
1607 cx: &AppContext,
1608 ) -> Option<Arc<dyn language::File>> {
1609 self.buffer.read(cx).read(cx).file_at(point).cloned()
1610 }
1611
1612 pub fn active_excerpt(
1613 &self,
1614 cx: &AppContext,
1615 ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
1616 self.buffer
1617 .read(cx)
1618 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1619 }
1620
1621 pub fn mode(&self) -> EditorMode {
1622 self.mode
1623 }
1624
1625 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1626 self.collaboration_hub.as_deref()
1627 }
1628
1629 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1630 self.collaboration_hub = Some(hub);
1631 }
1632
1633 pub fn set_custom_context_menu(
1634 &mut self,
1635 f: impl 'static
1636 + Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
1637 ) {
1638 self.custom_context_menu = Some(Box::new(f))
1639 }
1640
1641 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1642 self.completion_provider = provider;
1643 }
1644
1645 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1646 self.semantics_provider.clone()
1647 }
1648
1649 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1650 self.semantics_provider = provider;
1651 }
1652
1653 pub fn set_inline_completion_provider<T>(
1654 &mut self,
1655 provider: Option<Model<T>>,
1656 cx: &mut ViewContext<Self>,
1657 ) where
1658 T: InlineCompletionProvider,
1659 {
1660 self.inline_completion_provider =
1661 provider.map(|provider| RegisteredInlineCompletionProvider {
1662 _subscription: cx.observe(&provider, |this, _, cx| {
1663 if this.focus_handle.is_focused(cx) {
1664 this.update_visible_inline_completion(cx);
1665 }
1666 }),
1667 provider: Arc::new(provider),
1668 });
1669 self.refresh_inline_completion(false, false, cx);
1670 }
1671
1672 pub fn placeholder_text(&self, _cx: &WindowContext) -> Option<&str> {
1673 self.placeholder_text.as_deref()
1674 }
1675
1676 pub fn set_placeholder_text(
1677 &mut self,
1678 placeholder_text: impl Into<Arc<str>>,
1679 cx: &mut ViewContext<Self>,
1680 ) {
1681 let placeholder_text = Some(placeholder_text.into());
1682 if self.placeholder_text != placeholder_text {
1683 self.placeholder_text = placeholder_text;
1684 cx.notify();
1685 }
1686 }
1687
1688 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1689 self.cursor_shape = cursor_shape;
1690
1691 // Disrupt blink for immediate user feedback that the cursor shape has changed
1692 self.blink_manager.update(cx, BlinkManager::show_cursor);
1693
1694 cx.notify();
1695 }
1696
1697 pub fn set_current_line_highlight(
1698 &mut self,
1699 current_line_highlight: Option<CurrentLineHighlight>,
1700 ) {
1701 self.current_line_highlight = current_line_highlight;
1702 }
1703
1704 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1705 self.collapse_matches = collapse_matches;
1706 }
1707
1708 pub fn register_buffers_with_language_servers(&mut self, cx: &mut ViewContext<Self>) {
1709 let buffers = self.buffer.read(cx).all_buffers();
1710 let Some(lsp_store) = self.lsp_store(cx) else {
1711 return;
1712 };
1713 lsp_store.update(cx, |lsp_store, cx| {
1714 for buffer in buffers {
1715 self.registered_buffers
1716 .entry(buffer.read(cx).remote_id())
1717 .or_insert_with(|| {
1718 lsp_store.register_buffer_with_language_servers(&buffer, cx)
1719 });
1720 }
1721 })
1722 }
1723
1724 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1725 if self.collapse_matches {
1726 return range.start..range.start;
1727 }
1728 range.clone()
1729 }
1730
1731 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1732 if self.display_map.read(cx).clip_at_line_ends != clip {
1733 self.display_map
1734 .update(cx, |map, _| map.clip_at_line_ends = clip);
1735 }
1736 }
1737
1738 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1739 self.input_enabled = input_enabled;
1740 }
1741
1742 pub fn set_inline_completions_enabled(&mut self, enabled: bool, cx: &mut ViewContext<Self>) {
1743 self.enable_inline_completions = enabled;
1744 if !self.enable_inline_completions {
1745 self.take_active_inline_completion(cx);
1746 cx.notify();
1747 }
1748 }
1749
1750 pub fn set_autoindent(&mut self, autoindent: bool) {
1751 if autoindent {
1752 self.autoindent_mode = Some(AutoindentMode::EachLine);
1753 } else {
1754 self.autoindent_mode = None;
1755 }
1756 }
1757
1758 pub fn read_only(&self, cx: &AppContext) -> bool {
1759 self.read_only || self.buffer.read(cx).read_only()
1760 }
1761
1762 pub fn set_read_only(&mut self, read_only: bool) {
1763 self.read_only = read_only;
1764 }
1765
1766 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1767 self.use_autoclose = autoclose;
1768 }
1769
1770 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1771 self.use_auto_surround = auto_surround;
1772 }
1773
1774 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1775 self.auto_replace_emoji_shortcode = auto_replace;
1776 }
1777
1778 pub fn toggle_inline_completions(
1779 &mut self,
1780 _: &ToggleInlineCompletions,
1781 cx: &mut ViewContext<Self>,
1782 ) {
1783 if self.show_inline_completions_override.is_some() {
1784 self.set_show_inline_completions(None, cx);
1785 } else {
1786 let cursor = self.selections.newest_anchor().head();
1787 if let Some((buffer, cursor_buffer_position)) =
1788 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1789 {
1790 let show_inline_completions =
1791 !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx);
1792 self.set_show_inline_completions(Some(show_inline_completions), cx);
1793 }
1794 }
1795 }
1796
1797 pub fn set_show_inline_completions(
1798 &mut self,
1799 show_inline_completions: Option<bool>,
1800 cx: &mut ViewContext<Self>,
1801 ) {
1802 self.show_inline_completions_override = show_inline_completions;
1803 self.refresh_inline_completion(false, true, cx);
1804 }
1805
1806 pub fn inline_completions_enabled(&self, cx: &AppContext) -> bool {
1807 let cursor = self.selections.newest_anchor().head();
1808 if let Some((buffer, buffer_position)) =
1809 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
1810 {
1811 self.should_show_inline_completions(&buffer, buffer_position, cx)
1812 } else {
1813 false
1814 }
1815 }
1816
1817 fn should_show_inline_completions(
1818 &self,
1819 buffer: &Model<Buffer>,
1820 buffer_position: language::Anchor,
1821 cx: &AppContext,
1822 ) -> bool {
1823 if !self.snippet_stack.is_empty() {
1824 return false;
1825 }
1826
1827 if self.inline_completions_disabled_in_scope(buffer, buffer_position, cx) {
1828 return false;
1829 }
1830
1831 if let Some(provider) = self.inline_completion_provider() {
1832 if let Some(show_inline_completions) = self.show_inline_completions_override {
1833 show_inline_completions
1834 } else {
1835 self.mode == EditorMode::Full && provider.is_enabled(buffer, buffer_position, cx)
1836 }
1837 } else {
1838 false
1839 }
1840 }
1841
1842 fn inline_completions_disabled_in_scope(
1843 &self,
1844 buffer: &Model<Buffer>,
1845 buffer_position: language::Anchor,
1846 cx: &AppContext,
1847 ) -> bool {
1848 let snapshot = buffer.read(cx).snapshot();
1849 let settings = snapshot.settings_at(buffer_position, cx);
1850
1851 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
1852 return false;
1853 };
1854
1855 scope.override_name().map_or(false, |scope_name| {
1856 settings
1857 .inline_completions_disabled_in
1858 .iter()
1859 .any(|s| s == scope_name)
1860 })
1861 }
1862
1863 pub fn set_use_modal_editing(&mut self, to: bool) {
1864 self.use_modal_editing = to;
1865 }
1866
1867 pub fn use_modal_editing(&self) -> bool {
1868 self.use_modal_editing
1869 }
1870
1871 fn selections_did_change(
1872 &mut self,
1873 local: bool,
1874 old_cursor_position: &Anchor,
1875 show_completions: bool,
1876 cx: &mut ViewContext<Self>,
1877 ) {
1878 cx.invalidate_character_coordinates();
1879
1880 // Copy selections to primary selection buffer
1881 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
1882 if local {
1883 let selections = self.selections.all::<usize>(cx);
1884 let buffer_handle = self.buffer.read(cx).read(cx);
1885
1886 let mut text = String::new();
1887 for (index, selection) in selections.iter().enumerate() {
1888 let text_for_selection = buffer_handle
1889 .text_for_range(selection.start..selection.end)
1890 .collect::<String>();
1891
1892 text.push_str(&text_for_selection);
1893 if index != selections.len() - 1 {
1894 text.push('\n');
1895 }
1896 }
1897
1898 if !text.is_empty() {
1899 cx.write_to_primary(ClipboardItem::new_string(text));
1900 }
1901 }
1902
1903 if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
1904 self.buffer.update(cx, |buffer, cx| {
1905 buffer.set_active_selections(
1906 &self.selections.disjoint_anchors(),
1907 self.selections.line_mode,
1908 self.cursor_shape,
1909 cx,
1910 )
1911 });
1912 }
1913 let display_map = self
1914 .display_map
1915 .update(cx, |display_map, cx| display_map.snapshot(cx));
1916 let buffer = &display_map.buffer_snapshot;
1917 self.add_selections_state = None;
1918 self.select_next_state = None;
1919 self.select_prev_state = None;
1920 self.select_larger_syntax_node_stack.clear();
1921 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1922 self.snippet_stack
1923 .invalidate(&self.selections.disjoint_anchors(), buffer);
1924 self.take_rename(false, cx);
1925
1926 let new_cursor_position = self.selections.newest_anchor().head();
1927
1928 self.push_to_nav_history(
1929 *old_cursor_position,
1930 Some(new_cursor_position.to_point(buffer)),
1931 cx,
1932 );
1933
1934 if local {
1935 let new_cursor_position = self.selections.newest_anchor().head();
1936 let mut context_menu = self.context_menu.borrow_mut();
1937 let completion_menu = match context_menu.as_ref() {
1938 Some(CodeContextMenu::Completions(menu)) => Some(menu),
1939 _ => {
1940 *context_menu = None;
1941 None
1942 }
1943 };
1944
1945 if let Some(completion_menu) = completion_menu {
1946 let cursor_position = new_cursor_position.to_offset(buffer);
1947 let (word_range, kind) =
1948 buffer.surrounding_word(completion_menu.initial_position, true);
1949 if kind == Some(CharKind::Word)
1950 && word_range.to_inclusive().contains(&cursor_position)
1951 {
1952 let mut completion_menu = completion_menu.clone();
1953 drop(context_menu);
1954
1955 let query = Self::completion_query(buffer, cursor_position);
1956 cx.spawn(move |this, mut cx| async move {
1957 completion_menu
1958 .filter(query.as_deref(), cx.background_executor().clone())
1959 .await;
1960
1961 this.update(&mut cx, |this, cx| {
1962 let mut context_menu = this.context_menu.borrow_mut();
1963 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
1964 else {
1965 return;
1966 };
1967
1968 if menu.id > completion_menu.id {
1969 return;
1970 }
1971
1972 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
1973 drop(context_menu);
1974 cx.notify();
1975 })
1976 })
1977 .detach();
1978
1979 if show_completions {
1980 self.show_completions(&ShowCompletions { trigger: None }, cx);
1981 }
1982 } else {
1983 drop(context_menu);
1984 self.hide_context_menu(cx);
1985 }
1986 } else {
1987 drop(context_menu);
1988 }
1989
1990 hide_hover(self, cx);
1991
1992 if old_cursor_position.to_display_point(&display_map).row()
1993 != new_cursor_position.to_display_point(&display_map).row()
1994 {
1995 self.available_code_actions.take();
1996 }
1997 self.refresh_code_actions(cx);
1998 self.refresh_document_highlights(cx);
1999 refresh_matching_bracket_highlights(self, cx);
2000 self.update_visible_inline_completion(cx);
2001 linked_editing_ranges::refresh_linked_ranges(self, cx);
2002 if self.git_blame_inline_enabled {
2003 self.start_inline_blame_timer(cx);
2004 }
2005 }
2006
2007 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2008 cx.emit(EditorEvent::SelectionsChanged { local });
2009
2010 if self.selections.disjoint_anchors().len() == 1 {
2011 cx.emit(SearchEvent::ActiveMatchChanged)
2012 }
2013 cx.notify();
2014 }
2015
2016 pub fn change_selections<R>(
2017 &mut self,
2018 autoscroll: Option<Autoscroll>,
2019 cx: &mut ViewContext<Self>,
2020 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2021 ) -> R {
2022 self.change_selections_inner(autoscroll, true, cx, change)
2023 }
2024
2025 pub fn change_selections_inner<R>(
2026 &mut self,
2027 autoscroll: Option<Autoscroll>,
2028 request_completions: bool,
2029 cx: &mut ViewContext<Self>,
2030 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2031 ) -> R {
2032 let old_cursor_position = self.selections.newest_anchor().head();
2033 self.push_to_selection_history();
2034
2035 let (changed, result) = self.selections.change_with(cx, change);
2036
2037 if changed {
2038 if let Some(autoscroll) = autoscroll {
2039 self.request_autoscroll(autoscroll, cx);
2040 }
2041 self.selections_did_change(true, &old_cursor_position, request_completions, cx);
2042
2043 if self.should_open_signature_help_automatically(
2044 &old_cursor_position,
2045 self.signature_help_state.backspace_pressed(),
2046 cx,
2047 ) {
2048 self.show_signature_help(&ShowSignatureHelp, cx);
2049 }
2050 self.signature_help_state.set_backspace_pressed(false);
2051 }
2052
2053 result
2054 }
2055
2056 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2057 where
2058 I: IntoIterator<Item = (Range<S>, T)>,
2059 S: ToOffset,
2060 T: Into<Arc<str>>,
2061 {
2062 if self.read_only(cx) {
2063 return;
2064 }
2065
2066 self.buffer
2067 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2068 }
2069
2070 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
2071 where
2072 I: IntoIterator<Item = (Range<S>, T)>,
2073 S: ToOffset,
2074 T: Into<Arc<str>>,
2075 {
2076 if self.read_only(cx) {
2077 return;
2078 }
2079
2080 self.buffer.update(cx, |buffer, cx| {
2081 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2082 });
2083 }
2084
2085 pub fn edit_with_block_indent<I, S, T>(
2086 &mut self,
2087 edits: I,
2088 original_indent_columns: Vec<u32>,
2089 cx: &mut ViewContext<Self>,
2090 ) where
2091 I: IntoIterator<Item = (Range<S>, T)>,
2092 S: ToOffset,
2093 T: Into<Arc<str>>,
2094 {
2095 if self.read_only(cx) {
2096 return;
2097 }
2098
2099 self.buffer.update(cx, |buffer, cx| {
2100 buffer.edit(
2101 edits,
2102 Some(AutoindentMode::Block {
2103 original_indent_columns,
2104 }),
2105 cx,
2106 )
2107 });
2108 }
2109
2110 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
2111 self.hide_context_menu(cx);
2112
2113 match phase {
2114 SelectPhase::Begin {
2115 position,
2116 add,
2117 click_count,
2118 } => self.begin_selection(position, add, click_count, cx),
2119 SelectPhase::BeginColumnar {
2120 position,
2121 goal_column,
2122 reset,
2123 } => self.begin_columnar_selection(position, goal_column, reset, cx),
2124 SelectPhase::Extend {
2125 position,
2126 click_count,
2127 } => self.extend_selection(position, click_count, cx),
2128 SelectPhase::Update {
2129 position,
2130 goal_column,
2131 scroll_delta,
2132 } => self.update_selection(position, goal_column, scroll_delta, cx),
2133 SelectPhase::End => self.end_selection(cx),
2134 }
2135 }
2136
2137 fn extend_selection(
2138 &mut self,
2139 position: DisplayPoint,
2140 click_count: usize,
2141 cx: &mut ViewContext<Self>,
2142 ) {
2143 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2144 let tail = self.selections.newest::<usize>(cx).tail();
2145 self.begin_selection(position, false, click_count, cx);
2146
2147 let position = position.to_offset(&display_map, Bias::Left);
2148 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2149
2150 let mut pending_selection = self
2151 .selections
2152 .pending_anchor()
2153 .expect("extend_selection not called with pending selection");
2154 if position >= tail {
2155 pending_selection.start = tail_anchor;
2156 } else {
2157 pending_selection.end = tail_anchor;
2158 pending_selection.reversed = true;
2159 }
2160
2161 let mut pending_mode = self.selections.pending_mode().unwrap();
2162 match &mut pending_mode {
2163 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2164 _ => {}
2165 }
2166
2167 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
2168 s.set_pending(pending_selection, pending_mode)
2169 });
2170 }
2171
2172 fn begin_selection(
2173 &mut self,
2174 position: DisplayPoint,
2175 add: bool,
2176 click_count: usize,
2177 cx: &mut ViewContext<Self>,
2178 ) {
2179 if !self.focus_handle.is_focused(cx) {
2180 self.last_focused_descendant = None;
2181 cx.focus(&self.focus_handle);
2182 }
2183
2184 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2185 let buffer = &display_map.buffer_snapshot;
2186 let newest_selection = self.selections.newest_anchor().clone();
2187 let position = display_map.clip_point(position, Bias::Left);
2188
2189 let start;
2190 let end;
2191 let mode;
2192 let mut auto_scroll;
2193 match click_count {
2194 1 => {
2195 start = buffer.anchor_before(position.to_point(&display_map));
2196 end = start;
2197 mode = SelectMode::Character;
2198 auto_scroll = true;
2199 }
2200 2 => {
2201 let range = movement::surrounding_word(&display_map, position);
2202 start = buffer.anchor_before(range.start.to_point(&display_map));
2203 end = buffer.anchor_before(range.end.to_point(&display_map));
2204 mode = SelectMode::Word(start..end);
2205 auto_scroll = true;
2206 }
2207 3 => {
2208 let position = display_map
2209 .clip_point(position, Bias::Left)
2210 .to_point(&display_map);
2211 let line_start = display_map.prev_line_boundary(position).0;
2212 let next_line_start = buffer.clip_point(
2213 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2214 Bias::Left,
2215 );
2216 start = buffer.anchor_before(line_start);
2217 end = buffer.anchor_before(next_line_start);
2218 mode = SelectMode::Line(start..end);
2219 auto_scroll = true;
2220 }
2221 _ => {
2222 start = buffer.anchor_before(0);
2223 end = buffer.anchor_before(buffer.len());
2224 mode = SelectMode::All;
2225 auto_scroll = false;
2226 }
2227 }
2228 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2229
2230 let point_to_delete: Option<usize> = {
2231 let selected_points: Vec<Selection<Point>> =
2232 self.selections.disjoint_in_range(start..end, cx);
2233
2234 if !add || click_count > 1 {
2235 None
2236 } else if !selected_points.is_empty() {
2237 Some(selected_points[0].id)
2238 } else {
2239 let clicked_point_already_selected =
2240 self.selections.disjoint.iter().find(|selection| {
2241 selection.start.to_point(buffer) == start.to_point(buffer)
2242 || selection.end.to_point(buffer) == end.to_point(buffer)
2243 });
2244
2245 clicked_point_already_selected.map(|selection| selection.id)
2246 }
2247 };
2248
2249 let selections_count = self.selections.count();
2250
2251 self.change_selections(auto_scroll.then(Autoscroll::newest), cx, |s| {
2252 if let Some(point_to_delete) = point_to_delete {
2253 s.delete(point_to_delete);
2254
2255 if selections_count == 1 {
2256 s.set_pending_anchor_range(start..end, mode);
2257 }
2258 } else {
2259 if !add {
2260 s.clear_disjoint();
2261 } else if click_count > 1 {
2262 s.delete(newest_selection.id)
2263 }
2264
2265 s.set_pending_anchor_range(start..end, mode);
2266 }
2267 });
2268 }
2269
2270 fn begin_columnar_selection(
2271 &mut self,
2272 position: DisplayPoint,
2273 goal_column: u32,
2274 reset: bool,
2275 cx: &mut ViewContext<Self>,
2276 ) {
2277 if !self.focus_handle.is_focused(cx) {
2278 self.last_focused_descendant = None;
2279 cx.focus(&self.focus_handle);
2280 }
2281
2282 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2283
2284 if reset {
2285 let pointer_position = display_map
2286 .buffer_snapshot
2287 .anchor_before(position.to_point(&display_map));
2288
2289 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
2290 s.clear_disjoint();
2291 s.set_pending_anchor_range(
2292 pointer_position..pointer_position,
2293 SelectMode::Character,
2294 );
2295 });
2296 }
2297
2298 let tail = self.selections.newest::<Point>(cx).tail();
2299 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2300
2301 if !reset {
2302 self.select_columns(
2303 tail.to_display_point(&display_map),
2304 position,
2305 goal_column,
2306 &display_map,
2307 cx,
2308 );
2309 }
2310 }
2311
2312 fn update_selection(
2313 &mut self,
2314 position: DisplayPoint,
2315 goal_column: u32,
2316 scroll_delta: gpui::Point<f32>,
2317 cx: &mut ViewContext<Self>,
2318 ) {
2319 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2320
2321 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2322 let tail = tail.to_display_point(&display_map);
2323 self.select_columns(tail, position, goal_column, &display_map, cx);
2324 } else if let Some(mut pending) = self.selections.pending_anchor() {
2325 let buffer = self.buffer.read(cx).snapshot(cx);
2326 let head;
2327 let tail;
2328 let mode = self.selections.pending_mode().unwrap();
2329 match &mode {
2330 SelectMode::Character => {
2331 head = position.to_point(&display_map);
2332 tail = pending.tail().to_point(&buffer);
2333 }
2334 SelectMode::Word(original_range) => {
2335 let original_display_range = original_range.start.to_display_point(&display_map)
2336 ..original_range.end.to_display_point(&display_map);
2337 let original_buffer_range = original_display_range.start.to_point(&display_map)
2338 ..original_display_range.end.to_point(&display_map);
2339 if movement::is_inside_word(&display_map, position)
2340 || original_display_range.contains(&position)
2341 {
2342 let word_range = movement::surrounding_word(&display_map, position);
2343 if word_range.start < original_display_range.start {
2344 head = word_range.start.to_point(&display_map);
2345 } else {
2346 head = word_range.end.to_point(&display_map);
2347 }
2348 } else {
2349 head = position.to_point(&display_map);
2350 }
2351
2352 if head <= original_buffer_range.start {
2353 tail = original_buffer_range.end;
2354 } else {
2355 tail = original_buffer_range.start;
2356 }
2357 }
2358 SelectMode::Line(original_range) => {
2359 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2360
2361 let position = display_map
2362 .clip_point(position, Bias::Left)
2363 .to_point(&display_map);
2364 let line_start = display_map.prev_line_boundary(position).0;
2365 let next_line_start = buffer.clip_point(
2366 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2367 Bias::Left,
2368 );
2369
2370 if line_start < original_range.start {
2371 head = line_start
2372 } else {
2373 head = next_line_start
2374 }
2375
2376 if head <= original_range.start {
2377 tail = original_range.end;
2378 } else {
2379 tail = original_range.start;
2380 }
2381 }
2382 SelectMode::All => {
2383 return;
2384 }
2385 };
2386
2387 if head < tail {
2388 pending.start = buffer.anchor_before(head);
2389 pending.end = buffer.anchor_before(tail);
2390 pending.reversed = true;
2391 } else {
2392 pending.start = buffer.anchor_before(tail);
2393 pending.end = buffer.anchor_before(head);
2394 pending.reversed = false;
2395 }
2396
2397 self.change_selections(None, cx, |s| {
2398 s.set_pending(pending, mode);
2399 });
2400 } else {
2401 log::error!("update_selection dispatched with no pending selection");
2402 return;
2403 }
2404
2405 self.apply_scroll_delta(scroll_delta, cx);
2406 cx.notify();
2407 }
2408
2409 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2410 self.columnar_selection_tail.take();
2411 if self.selections.pending_anchor().is_some() {
2412 let selections = self.selections.all::<usize>(cx);
2413 self.change_selections(None, cx, |s| {
2414 s.select(selections);
2415 s.clear_pending();
2416 });
2417 }
2418 }
2419
2420 fn select_columns(
2421 &mut self,
2422 tail: DisplayPoint,
2423 head: DisplayPoint,
2424 goal_column: u32,
2425 display_map: &DisplaySnapshot,
2426 cx: &mut ViewContext<Self>,
2427 ) {
2428 let start_row = cmp::min(tail.row(), head.row());
2429 let end_row = cmp::max(tail.row(), head.row());
2430 let start_column = cmp::min(tail.column(), goal_column);
2431 let end_column = cmp::max(tail.column(), goal_column);
2432 let reversed = start_column < tail.column();
2433
2434 let selection_ranges = (start_row.0..=end_row.0)
2435 .map(DisplayRow)
2436 .filter_map(|row| {
2437 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2438 let start = display_map
2439 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2440 .to_point(display_map);
2441 let end = display_map
2442 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2443 .to_point(display_map);
2444 if reversed {
2445 Some(end..start)
2446 } else {
2447 Some(start..end)
2448 }
2449 } else {
2450 None
2451 }
2452 })
2453 .collect::<Vec<_>>();
2454
2455 self.change_selections(None, cx, |s| {
2456 s.select_ranges(selection_ranges);
2457 });
2458 cx.notify();
2459 }
2460
2461 pub fn has_pending_nonempty_selection(&self) -> bool {
2462 let pending_nonempty_selection = match self.selections.pending_anchor() {
2463 Some(Selection { start, end, .. }) => start != end,
2464 None => false,
2465 };
2466
2467 pending_nonempty_selection
2468 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2469 }
2470
2471 pub fn has_pending_selection(&self) -> bool {
2472 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2473 }
2474
2475 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2476 if self.clear_expanded_diff_hunks(cx) {
2477 cx.notify();
2478 return;
2479 }
2480 if self.dismiss_menus_and_popups(true, cx) {
2481 return;
2482 }
2483
2484 if self.mode == EditorMode::Full
2485 && self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel())
2486 {
2487 return;
2488 }
2489
2490 cx.propagate();
2491 }
2492
2493 pub fn dismiss_menus_and_popups(
2494 &mut self,
2495 should_report_inline_completion_event: bool,
2496 cx: &mut ViewContext<Self>,
2497 ) -> bool {
2498 if self.take_rename(false, cx).is_some() {
2499 return true;
2500 }
2501
2502 if hide_hover(self, cx) {
2503 return true;
2504 }
2505
2506 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2507 return true;
2508 }
2509
2510 if self.hide_context_menu(cx).is_some() {
2511 if self.show_inline_completions_in_menu(cx) && self.has_active_inline_completion() {
2512 self.update_visible_inline_completion(cx);
2513 }
2514 return true;
2515 }
2516
2517 if self.mouse_context_menu.take().is_some() {
2518 return true;
2519 }
2520
2521 if self.discard_inline_completion(should_report_inline_completion_event, cx) {
2522 return true;
2523 }
2524
2525 if self.snippet_stack.pop().is_some() {
2526 return true;
2527 }
2528
2529 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2530 self.dismiss_diagnostics(cx);
2531 return true;
2532 }
2533
2534 false
2535 }
2536
2537 fn linked_editing_ranges_for(
2538 &self,
2539 selection: Range<text::Anchor>,
2540 cx: &AppContext,
2541 ) -> Option<HashMap<Model<Buffer>, Vec<Range<text::Anchor>>>> {
2542 if self.linked_edit_ranges.is_empty() {
2543 return None;
2544 }
2545 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2546 selection.end.buffer_id.and_then(|end_buffer_id| {
2547 if selection.start.buffer_id != Some(end_buffer_id) {
2548 return None;
2549 }
2550 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2551 let snapshot = buffer.read(cx).snapshot();
2552 self.linked_edit_ranges
2553 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2554 .map(|ranges| (ranges, snapshot, buffer))
2555 })?;
2556 use text::ToOffset as TO;
2557 // find offset from the start of current range to current cursor position
2558 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2559
2560 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2561 let start_difference = start_offset - start_byte_offset;
2562 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2563 let end_difference = end_offset - start_byte_offset;
2564 // Current range has associated linked ranges.
2565 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2566 for range in linked_ranges.iter() {
2567 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2568 let end_offset = start_offset + end_difference;
2569 let start_offset = start_offset + start_difference;
2570 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2571 continue;
2572 }
2573 if self.selections.disjoint_anchor_ranges().iter().any(|s| {
2574 if s.start.buffer_id != selection.start.buffer_id
2575 || s.end.buffer_id != selection.end.buffer_id
2576 {
2577 return false;
2578 }
2579 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2580 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2581 }) {
2582 continue;
2583 }
2584 let start = buffer_snapshot.anchor_after(start_offset);
2585 let end = buffer_snapshot.anchor_after(end_offset);
2586 linked_edits
2587 .entry(buffer.clone())
2588 .or_default()
2589 .push(start..end);
2590 }
2591 Some(linked_edits)
2592 }
2593
2594 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2595 let text: Arc<str> = text.into();
2596
2597 if self.read_only(cx) {
2598 return;
2599 }
2600
2601 let selections = self.selections.all_adjusted(cx);
2602 let mut bracket_inserted = false;
2603 let mut edits = Vec::new();
2604 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2605 let mut new_selections = Vec::with_capacity(selections.len());
2606 let mut new_autoclose_regions = Vec::new();
2607 let snapshot = self.buffer.read(cx).read(cx);
2608
2609 for (selection, autoclose_region) in
2610 self.selections_with_autoclose_regions(selections, &snapshot)
2611 {
2612 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2613 // Determine if the inserted text matches the opening or closing
2614 // bracket of any of this language's bracket pairs.
2615 let mut bracket_pair = None;
2616 let mut is_bracket_pair_start = false;
2617 let mut is_bracket_pair_end = false;
2618 if !text.is_empty() {
2619 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2620 // and they are removing the character that triggered IME popup.
2621 for (pair, enabled) in scope.brackets() {
2622 if !pair.close && !pair.surround {
2623 continue;
2624 }
2625
2626 if enabled && pair.start.ends_with(text.as_ref()) {
2627 let prefix_len = pair.start.len() - text.len();
2628 let preceding_text_matches_prefix = prefix_len == 0
2629 || (selection.start.column >= (prefix_len as u32)
2630 && snapshot.contains_str_at(
2631 Point::new(
2632 selection.start.row,
2633 selection.start.column - (prefix_len as u32),
2634 ),
2635 &pair.start[..prefix_len],
2636 ));
2637 if preceding_text_matches_prefix {
2638 bracket_pair = Some(pair.clone());
2639 is_bracket_pair_start = true;
2640 break;
2641 }
2642 }
2643 if pair.end.as_str() == text.as_ref() {
2644 bracket_pair = Some(pair.clone());
2645 is_bracket_pair_end = true;
2646 break;
2647 }
2648 }
2649 }
2650
2651 if let Some(bracket_pair) = bracket_pair {
2652 let snapshot_settings = snapshot.settings_at(selection.start, cx);
2653 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2654 let auto_surround =
2655 self.use_auto_surround && snapshot_settings.use_auto_surround;
2656 if selection.is_empty() {
2657 if is_bracket_pair_start {
2658 // If the inserted text is a suffix of an opening bracket and the
2659 // selection is preceded by the rest of the opening bracket, then
2660 // insert the closing bracket.
2661 let following_text_allows_autoclose = snapshot
2662 .chars_at(selection.start)
2663 .next()
2664 .map_or(true, |c| scope.should_autoclose_before(c));
2665
2666 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2667 && bracket_pair.start.len() == 1
2668 {
2669 let target = bracket_pair.start.chars().next().unwrap();
2670 let current_line_count = snapshot
2671 .reversed_chars_at(selection.start)
2672 .take_while(|&c| c != '\n')
2673 .filter(|&c| c == target)
2674 .count();
2675 current_line_count % 2 == 1
2676 } else {
2677 false
2678 };
2679
2680 if autoclose
2681 && bracket_pair.close
2682 && following_text_allows_autoclose
2683 && !is_closing_quote
2684 {
2685 let anchor = snapshot.anchor_before(selection.end);
2686 new_selections.push((selection.map(|_| anchor), text.len()));
2687 new_autoclose_regions.push((
2688 anchor,
2689 text.len(),
2690 selection.id,
2691 bracket_pair.clone(),
2692 ));
2693 edits.push((
2694 selection.range(),
2695 format!("{}{}", text, bracket_pair.end).into(),
2696 ));
2697 bracket_inserted = true;
2698 continue;
2699 }
2700 }
2701
2702 if let Some(region) = autoclose_region {
2703 // If the selection is followed by an auto-inserted closing bracket,
2704 // then don't insert that closing bracket again; just move the selection
2705 // past the closing bracket.
2706 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2707 && text.as_ref() == region.pair.end.as_str();
2708 if should_skip {
2709 let anchor = snapshot.anchor_after(selection.end);
2710 new_selections
2711 .push((selection.map(|_| anchor), region.pair.end.len()));
2712 continue;
2713 }
2714 }
2715
2716 let always_treat_brackets_as_autoclosed = snapshot
2717 .settings_at(selection.start, cx)
2718 .always_treat_brackets_as_autoclosed;
2719 if always_treat_brackets_as_autoclosed
2720 && is_bracket_pair_end
2721 && snapshot.contains_str_at(selection.end, text.as_ref())
2722 {
2723 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2724 // and the inserted text is a closing bracket and the selection is followed
2725 // by the closing bracket then move the selection past the closing bracket.
2726 let anchor = snapshot.anchor_after(selection.end);
2727 new_selections.push((selection.map(|_| anchor), text.len()));
2728 continue;
2729 }
2730 }
2731 // If an opening bracket is 1 character long and is typed while
2732 // text is selected, then surround that text with the bracket pair.
2733 else if auto_surround
2734 && bracket_pair.surround
2735 && is_bracket_pair_start
2736 && bracket_pair.start.chars().count() == 1
2737 {
2738 edits.push((selection.start..selection.start, text.clone()));
2739 edits.push((
2740 selection.end..selection.end,
2741 bracket_pair.end.as_str().into(),
2742 ));
2743 bracket_inserted = true;
2744 new_selections.push((
2745 Selection {
2746 id: selection.id,
2747 start: snapshot.anchor_after(selection.start),
2748 end: snapshot.anchor_before(selection.end),
2749 reversed: selection.reversed,
2750 goal: selection.goal,
2751 },
2752 0,
2753 ));
2754 continue;
2755 }
2756 }
2757 }
2758
2759 if self.auto_replace_emoji_shortcode
2760 && selection.is_empty()
2761 && text.as_ref().ends_with(':')
2762 {
2763 if let Some(possible_emoji_short_code) =
2764 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
2765 {
2766 if !possible_emoji_short_code.is_empty() {
2767 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
2768 let emoji_shortcode_start = Point::new(
2769 selection.start.row,
2770 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
2771 );
2772
2773 // Remove shortcode from buffer
2774 edits.push((
2775 emoji_shortcode_start..selection.start,
2776 "".to_string().into(),
2777 ));
2778 new_selections.push((
2779 Selection {
2780 id: selection.id,
2781 start: snapshot.anchor_after(emoji_shortcode_start),
2782 end: snapshot.anchor_before(selection.start),
2783 reversed: selection.reversed,
2784 goal: selection.goal,
2785 },
2786 0,
2787 ));
2788
2789 // Insert emoji
2790 let selection_start_anchor = snapshot.anchor_after(selection.start);
2791 new_selections.push((selection.map(|_| selection_start_anchor), 0));
2792 edits.push((selection.start..selection.end, emoji.to_string().into()));
2793
2794 continue;
2795 }
2796 }
2797 }
2798 }
2799
2800 // If not handling any auto-close operation, then just replace the selected
2801 // text with the given input and move the selection to the end of the
2802 // newly inserted text.
2803 let anchor = snapshot.anchor_after(selection.end);
2804 if !self.linked_edit_ranges.is_empty() {
2805 let start_anchor = snapshot.anchor_before(selection.start);
2806
2807 let is_word_char = text.chars().next().map_or(true, |char| {
2808 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
2809 classifier.is_word(char)
2810 });
2811
2812 if is_word_char {
2813 if let Some(ranges) = self
2814 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
2815 {
2816 for (buffer, edits) in ranges {
2817 linked_edits
2818 .entry(buffer.clone())
2819 .or_default()
2820 .extend(edits.into_iter().map(|range| (range, text.clone())));
2821 }
2822 }
2823 }
2824 }
2825
2826 new_selections.push((selection.map(|_| anchor), 0));
2827 edits.push((selection.start..selection.end, text.clone()));
2828 }
2829
2830 drop(snapshot);
2831
2832 self.transact(cx, |this, cx| {
2833 this.buffer.update(cx, |buffer, cx| {
2834 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2835 });
2836 for (buffer, edits) in linked_edits {
2837 buffer.update(cx, |buffer, cx| {
2838 let snapshot = buffer.snapshot();
2839 let edits = edits
2840 .into_iter()
2841 .map(|(range, text)| {
2842 use text::ToPoint as TP;
2843 let end_point = TP::to_point(&range.end, &snapshot);
2844 let start_point = TP::to_point(&range.start, &snapshot);
2845 (start_point..end_point, text)
2846 })
2847 .sorted_by_key(|(range, _)| range.start)
2848 .collect::<Vec<_>>();
2849 buffer.edit(edits, None, cx);
2850 })
2851 }
2852 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2853 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2854 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
2855 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
2856 .zip(new_selection_deltas)
2857 .map(|(selection, delta)| Selection {
2858 id: selection.id,
2859 start: selection.start + delta,
2860 end: selection.end + delta,
2861 reversed: selection.reversed,
2862 goal: SelectionGoal::None,
2863 })
2864 .collect::<Vec<_>>();
2865
2866 let mut i = 0;
2867 for (position, delta, selection_id, pair) in new_autoclose_regions {
2868 let position = position.to_offset(&map.buffer_snapshot) + delta;
2869 let start = map.buffer_snapshot.anchor_before(position);
2870 let end = map.buffer_snapshot.anchor_after(position);
2871 while let Some(existing_state) = this.autoclose_regions.get(i) {
2872 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
2873 Ordering::Less => i += 1,
2874 Ordering::Greater => break,
2875 Ordering::Equal => {
2876 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
2877 Ordering::Less => i += 1,
2878 Ordering::Equal => break,
2879 Ordering::Greater => break,
2880 }
2881 }
2882 }
2883 }
2884 this.autoclose_regions.insert(
2885 i,
2886 AutocloseRegion {
2887 selection_id,
2888 range: start..end,
2889 pair,
2890 },
2891 );
2892 }
2893
2894 let had_active_inline_completion = this.has_active_inline_completion();
2895 this.change_selections_inner(Some(Autoscroll::fit()), false, cx, |s| {
2896 s.select(new_selections)
2897 });
2898
2899 if !bracket_inserted {
2900 if let Some(on_type_format_task) =
2901 this.trigger_on_type_formatting(text.to_string(), cx)
2902 {
2903 on_type_format_task.detach_and_log_err(cx);
2904 }
2905 }
2906
2907 let editor_settings = EditorSettings::get_global(cx);
2908 if bracket_inserted
2909 && (editor_settings.auto_signature_help
2910 || editor_settings.show_signature_help_after_edits)
2911 {
2912 this.show_signature_help(&ShowSignatureHelp, cx);
2913 }
2914
2915 let trigger_in_words =
2916 this.show_inline_completions_in_menu(cx) || !had_active_inline_completion;
2917 this.trigger_completion_on_input(&text, trigger_in_words, cx);
2918 linked_editing_ranges::refresh_linked_ranges(this, cx);
2919 this.refresh_inline_completion(true, false, cx);
2920 });
2921 }
2922
2923 fn find_possible_emoji_shortcode_at_position(
2924 snapshot: &MultiBufferSnapshot,
2925 position: Point,
2926 ) -> Option<String> {
2927 let mut chars = Vec::new();
2928 let mut found_colon = false;
2929 for char in snapshot.reversed_chars_at(position).take(100) {
2930 // Found a possible emoji shortcode in the middle of the buffer
2931 if found_colon {
2932 if char.is_whitespace() {
2933 chars.reverse();
2934 return Some(chars.iter().collect());
2935 }
2936 // If the previous character is not a whitespace, we are in the middle of a word
2937 // and we only want to complete the shortcode if the word is made up of other emojis
2938 let mut containing_word = String::new();
2939 for ch in snapshot
2940 .reversed_chars_at(position)
2941 .skip(chars.len() + 1)
2942 .take(100)
2943 {
2944 if ch.is_whitespace() {
2945 break;
2946 }
2947 containing_word.push(ch);
2948 }
2949 let containing_word = containing_word.chars().rev().collect::<String>();
2950 if util::word_consists_of_emojis(containing_word.as_str()) {
2951 chars.reverse();
2952 return Some(chars.iter().collect());
2953 }
2954 }
2955
2956 if char.is_whitespace() || !char.is_ascii() {
2957 return None;
2958 }
2959 if char == ':' {
2960 found_colon = true;
2961 } else {
2962 chars.push(char);
2963 }
2964 }
2965 // Found a possible emoji shortcode at the beginning of the buffer
2966 chars.reverse();
2967 Some(chars.iter().collect())
2968 }
2969
2970 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2971 self.transact(cx, |this, cx| {
2972 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2973 let selections = this.selections.all::<usize>(cx);
2974 let multi_buffer = this.buffer.read(cx);
2975 let buffer = multi_buffer.snapshot(cx);
2976 selections
2977 .iter()
2978 .map(|selection| {
2979 let start_point = selection.start.to_point(&buffer);
2980 let mut indent =
2981 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
2982 indent.len = cmp::min(indent.len, start_point.column);
2983 let start = selection.start;
2984 let end = selection.end;
2985 let selection_is_empty = start == end;
2986 let language_scope = buffer.language_scope_at(start);
2987 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2988 &language_scope
2989 {
2990 let leading_whitespace_len = buffer
2991 .reversed_chars_at(start)
2992 .take_while(|c| c.is_whitespace() && *c != '\n')
2993 .map(|c| c.len_utf8())
2994 .sum::<usize>();
2995
2996 let trailing_whitespace_len = buffer
2997 .chars_at(end)
2998 .take_while(|c| c.is_whitespace() && *c != '\n')
2999 .map(|c| c.len_utf8())
3000 .sum::<usize>();
3001
3002 let insert_extra_newline =
3003 language.brackets().any(|(pair, enabled)| {
3004 let pair_start = pair.start.trim_end();
3005 let pair_end = pair.end.trim_start();
3006
3007 enabled
3008 && pair.newline
3009 && buffer.contains_str_at(
3010 end + trailing_whitespace_len,
3011 pair_end,
3012 )
3013 && buffer.contains_str_at(
3014 (start - leading_whitespace_len)
3015 .saturating_sub(pair_start.len()),
3016 pair_start,
3017 )
3018 });
3019
3020 // Comment extension on newline is allowed only for cursor selections
3021 let comment_delimiter = maybe!({
3022 if !selection_is_empty {
3023 return None;
3024 }
3025
3026 if !multi_buffer.settings_at(0, cx).extend_comment_on_newline {
3027 return None;
3028 }
3029
3030 let delimiters = language.line_comment_prefixes();
3031 let max_len_of_delimiter =
3032 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3033 let (snapshot, range) =
3034 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3035
3036 let mut index_of_first_non_whitespace = 0;
3037 let comment_candidate = snapshot
3038 .chars_for_range(range)
3039 .skip_while(|c| {
3040 let should_skip = c.is_whitespace();
3041 if should_skip {
3042 index_of_first_non_whitespace += 1;
3043 }
3044 should_skip
3045 })
3046 .take(max_len_of_delimiter)
3047 .collect::<String>();
3048 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3049 comment_candidate.starts_with(comment_prefix.as_ref())
3050 })?;
3051 let cursor_is_placed_after_comment_marker =
3052 index_of_first_non_whitespace + comment_prefix.len()
3053 <= start_point.column as usize;
3054 if cursor_is_placed_after_comment_marker {
3055 Some(comment_prefix.clone())
3056 } else {
3057 None
3058 }
3059 });
3060 (comment_delimiter, insert_extra_newline)
3061 } else {
3062 (None, false)
3063 };
3064
3065 let capacity_for_delimiter = comment_delimiter
3066 .as_deref()
3067 .map(str::len)
3068 .unwrap_or_default();
3069 let mut new_text =
3070 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3071 new_text.push('\n');
3072 new_text.extend(indent.chars());
3073 if let Some(delimiter) = &comment_delimiter {
3074 new_text.push_str(delimiter);
3075 }
3076 if insert_extra_newline {
3077 new_text = new_text.repeat(2);
3078 }
3079
3080 let anchor = buffer.anchor_after(end);
3081 let new_selection = selection.map(|_| anchor);
3082 (
3083 (start..end, new_text),
3084 (insert_extra_newline, new_selection),
3085 )
3086 })
3087 .unzip()
3088 };
3089
3090 this.edit_with_autoindent(edits, cx);
3091 let buffer = this.buffer.read(cx).snapshot(cx);
3092 let new_selections = selection_fixup_info
3093 .into_iter()
3094 .map(|(extra_newline_inserted, new_selection)| {
3095 let mut cursor = new_selection.end.to_point(&buffer);
3096 if extra_newline_inserted {
3097 cursor.row -= 1;
3098 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3099 }
3100 new_selection.map(|_| cursor)
3101 })
3102 .collect();
3103
3104 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
3105 this.refresh_inline_completion(true, false, cx);
3106 });
3107 }
3108
3109 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
3110 let buffer = self.buffer.read(cx);
3111 let snapshot = buffer.snapshot(cx);
3112
3113 let mut edits = Vec::new();
3114 let mut rows = Vec::new();
3115
3116 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3117 let cursor = selection.head();
3118 let row = cursor.row;
3119
3120 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3121
3122 let newline = "\n".to_string();
3123 edits.push((start_of_line..start_of_line, newline));
3124
3125 rows.push(row + rows_inserted as u32);
3126 }
3127
3128 self.transact(cx, |editor, cx| {
3129 editor.edit(edits, cx);
3130
3131 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3132 let mut index = 0;
3133 s.move_cursors_with(|map, _, _| {
3134 let row = rows[index];
3135 index += 1;
3136
3137 let point = Point::new(row, 0);
3138 let boundary = map.next_line_boundary(point).1;
3139 let clipped = map.clip_point(boundary, Bias::Left);
3140
3141 (clipped, SelectionGoal::None)
3142 });
3143 });
3144
3145 let mut indent_edits = Vec::new();
3146 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3147 for row in rows {
3148 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3149 for (row, indent) in indents {
3150 if indent.len == 0 {
3151 continue;
3152 }
3153
3154 let text = match indent.kind {
3155 IndentKind::Space => " ".repeat(indent.len as usize),
3156 IndentKind::Tab => "\t".repeat(indent.len as usize),
3157 };
3158 let point = Point::new(row.0, 0);
3159 indent_edits.push((point..point, text));
3160 }
3161 }
3162 editor.edit(indent_edits, cx);
3163 });
3164 }
3165
3166 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
3167 let buffer = self.buffer.read(cx);
3168 let snapshot = buffer.snapshot(cx);
3169
3170 let mut edits = Vec::new();
3171 let mut rows = Vec::new();
3172 let mut rows_inserted = 0;
3173
3174 for selection in self.selections.all_adjusted(cx) {
3175 let cursor = selection.head();
3176 let row = cursor.row;
3177
3178 let point = Point::new(row + 1, 0);
3179 let start_of_line = snapshot.clip_point(point, Bias::Left);
3180
3181 let newline = "\n".to_string();
3182 edits.push((start_of_line..start_of_line, newline));
3183
3184 rows_inserted += 1;
3185 rows.push(row + rows_inserted);
3186 }
3187
3188 self.transact(cx, |editor, cx| {
3189 editor.edit(edits, cx);
3190
3191 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
3192 let mut index = 0;
3193 s.move_cursors_with(|map, _, _| {
3194 let row = rows[index];
3195 index += 1;
3196
3197 let point = Point::new(row, 0);
3198 let boundary = map.next_line_boundary(point).1;
3199 let clipped = map.clip_point(boundary, Bias::Left);
3200
3201 (clipped, SelectionGoal::None)
3202 });
3203 });
3204
3205 let mut indent_edits = Vec::new();
3206 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3207 for row in rows {
3208 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3209 for (row, indent) in indents {
3210 if indent.len == 0 {
3211 continue;
3212 }
3213
3214 let text = match indent.kind {
3215 IndentKind::Space => " ".repeat(indent.len as usize),
3216 IndentKind::Tab => "\t".repeat(indent.len as usize),
3217 };
3218 let point = Point::new(row.0, 0);
3219 indent_edits.push((point..point, text));
3220 }
3221 }
3222 editor.edit(indent_edits, cx);
3223 });
3224 }
3225
3226 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
3227 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3228 original_indent_columns: Vec::new(),
3229 });
3230 self.insert_with_autoindent_mode(text, autoindent, cx);
3231 }
3232
3233 fn insert_with_autoindent_mode(
3234 &mut self,
3235 text: &str,
3236 autoindent_mode: Option<AutoindentMode>,
3237 cx: &mut ViewContext<Self>,
3238 ) {
3239 if self.read_only(cx) {
3240 return;
3241 }
3242
3243 let text: Arc<str> = text.into();
3244 self.transact(cx, |this, cx| {
3245 let old_selections = this.selections.all_adjusted(cx);
3246 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3247 let anchors = {
3248 let snapshot = buffer.read(cx);
3249 old_selections
3250 .iter()
3251 .map(|s| {
3252 let anchor = snapshot.anchor_after(s.head());
3253 s.map(|_| anchor)
3254 })
3255 .collect::<Vec<_>>()
3256 };
3257 buffer.edit(
3258 old_selections
3259 .iter()
3260 .map(|s| (s.start..s.end, text.clone())),
3261 autoindent_mode,
3262 cx,
3263 );
3264 anchors
3265 });
3266
3267 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3268 s.select_anchors(selection_anchors);
3269 })
3270 });
3271 }
3272
3273 fn trigger_completion_on_input(
3274 &mut self,
3275 text: &str,
3276 trigger_in_words: bool,
3277 cx: &mut ViewContext<Self>,
3278 ) {
3279 if self.is_completion_trigger(text, trigger_in_words, cx) {
3280 self.show_completions(
3281 &ShowCompletions {
3282 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3283 },
3284 cx,
3285 );
3286 } else {
3287 self.hide_context_menu(cx);
3288 }
3289 }
3290
3291 fn is_completion_trigger(
3292 &self,
3293 text: &str,
3294 trigger_in_words: bool,
3295 cx: &mut ViewContext<Self>,
3296 ) -> bool {
3297 let position = self.selections.newest_anchor().head();
3298 let multibuffer = self.buffer.read(cx);
3299 let Some(buffer) = position
3300 .buffer_id
3301 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3302 else {
3303 return false;
3304 };
3305
3306 if let Some(completion_provider) = &self.completion_provider {
3307 completion_provider.is_completion_trigger(
3308 &buffer,
3309 position.text_anchor,
3310 text,
3311 trigger_in_words,
3312 cx,
3313 )
3314 } else {
3315 false
3316 }
3317 }
3318
3319 /// If any empty selections is touching the start of its innermost containing autoclose
3320 /// region, expand it to select the brackets.
3321 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
3322 let selections = self.selections.all::<usize>(cx);
3323 let buffer = self.buffer.read(cx).read(cx);
3324 let new_selections = self
3325 .selections_with_autoclose_regions(selections, &buffer)
3326 .map(|(mut selection, region)| {
3327 if !selection.is_empty() {
3328 return selection;
3329 }
3330
3331 if let Some(region) = region {
3332 let mut range = region.range.to_offset(&buffer);
3333 if selection.start == range.start && range.start >= region.pair.start.len() {
3334 range.start -= region.pair.start.len();
3335 if buffer.contains_str_at(range.start, ®ion.pair.start)
3336 && buffer.contains_str_at(range.end, ®ion.pair.end)
3337 {
3338 range.end += region.pair.end.len();
3339 selection.start = range.start;
3340 selection.end = range.end;
3341
3342 return selection;
3343 }
3344 }
3345 }
3346
3347 let always_treat_brackets_as_autoclosed = buffer
3348 .settings_at(selection.start, cx)
3349 .always_treat_brackets_as_autoclosed;
3350
3351 if !always_treat_brackets_as_autoclosed {
3352 return selection;
3353 }
3354
3355 if let Some(scope) = buffer.language_scope_at(selection.start) {
3356 for (pair, enabled) in scope.brackets() {
3357 if !enabled || !pair.close {
3358 continue;
3359 }
3360
3361 if buffer.contains_str_at(selection.start, &pair.end) {
3362 let pair_start_len = pair.start.len();
3363 if buffer.contains_str_at(
3364 selection.start.saturating_sub(pair_start_len),
3365 &pair.start,
3366 ) {
3367 selection.start -= pair_start_len;
3368 selection.end += pair.end.len();
3369
3370 return selection;
3371 }
3372 }
3373 }
3374 }
3375
3376 selection
3377 })
3378 .collect();
3379
3380 drop(buffer);
3381 self.change_selections(None, cx, |selections| selections.select(new_selections));
3382 }
3383
3384 /// Iterate the given selections, and for each one, find the smallest surrounding
3385 /// autoclose region. This uses the ordering of the selections and the autoclose
3386 /// regions to avoid repeated comparisons.
3387 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3388 &'a self,
3389 selections: impl IntoIterator<Item = Selection<D>>,
3390 buffer: &'a MultiBufferSnapshot,
3391 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3392 let mut i = 0;
3393 let mut regions = self.autoclose_regions.as_slice();
3394 selections.into_iter().map(move |selection| {
3395 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3396
3397 let mut enclosing = None;
3398 while let Some(pair_state) = regions.get(i) {
3399 if pair_state.range.end.to_offset(buffer) < range.start {
3400 regions = ®ions[i + 1..];
3401 i = 0;
3402 } else if pair_state.range.start.to_offset(buffer) > range.end {
3403 break;
3404 } else {
3405 if pair_state.selection_id == selection.id {
3406 enclosing = Some(pair_state);
3407 }
3408 i += 1;
3409 }
3410 }
3411
3412 (selection, enclosing)
3413 })
3414 }
3415
3416 /// Remove any autoclose regions that no longer contain their selection.
3417 fn invalidate_autoclose_regions(
3418 &mut self,
3419 mut selections: &[Selection<Anchor>],
3420 buffer: &MultiBufferSnapshot,
3421 ) {
3422 self.autoclose_regions.retain(|state| {
3423 let mut i = 0;
3424 while let Some(selection) = selections.get(i) {
3425 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3426 selections = &selections[1..];
3427 continue;
3428 }
3429 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3430 break;
3431 }
3432 if selection.id == state.selection_id {
3433 return true;
3434 } else {
3435 i += 1;
3436 }
3437 }
3438 false
3439 });
3440 }
3441
3442 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3443 let offset = position.to_offset(buffer);
3444 let (word_range, kind) = buffer.surrounding_word(offset, true);
3445 if offset > word_range.start && kind == Some(CharKind::Word) {
3446 Some(
3447 buffer
3448 .text_for_range(word_range.start..offset)
3449 .collect::<String>(),
3450 )
3451 } else {
3452 None
3453 }
3454 }
3455
3456 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
3457 self.refresh_inlay_hints(
3458 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
3459 cx,
3460 );
3461 }
3462
3463 pub fn inlay_hints_enabled(&self) -> bool {
3464 self.inlay_hint_cache.enabled
3465 }
3466
3467 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
3468 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3469 return;
3470 }
3471
3472 let reason_description = reason.description();
3473 let ignore_debounce = matches!(
3474 reason,
3475 InlayHintRefreshReason::SettingsChange(_)
3476 | InlayHintRefreshReason::Toggle(_)
3477 | InlayHintRefreshReason::ExcerptsRemoved(_)
3478 );
3479 let (invalidate_cache, required_languages) = match reason {
3480 InlayHintRefreshReason::Toggle(enabled) => {
3481 self.inlay_hint_cache.enabled = enabled;
3482 if enabled {
3483 (InvalidationStrategy::RefreshRequested, None)
3484 } else {
3485 self.inlay_hint_cache.clear();
3486 self.splice_inlays(
3487 self.visible_inlay_hints(cx)
3488 .iter()
3489 .map(|inlay| inlay.id)
3490 .collect(),
3491 Vec::new(),
3492 cx,
3493 );
3494 return;
3495 }
3496 }
3497 InlayHintRefreshReason::SettingsChange(new_settings) => {
3498 match self.inlay_hint_cache.update_settings(
3499 &self.buffer,
3500 new_settings,
3501 self.visible_inlay_hints(cx),
3502 cx,
3503 ) {
3504 ControlFlow::Break(Some(InlaySplice {
3505 to_remove,
3506 to_insert,
3507 })) => {
3508 self.splice_inlays(to_remove, to_insert, cx);
3509 return;
3510 }
3511 ControlFlow::Break(None) => return,
3512 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3513 }
3514 }
3515 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3516 if let Some(InlaySplice {
3517 to_remove,
3518 to_insert,
3519 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3520 {
3521 self.splice_inlays(to_remove, to_insert, cx);
3522 }
3523 return;
3524 }
3525 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3526 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3527 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3528 }
3529 InlayHintRefreshReason::RefreshRequested => {
3530 (InvalidationStrategy::RefreshRequested, None)
3531 }
3532 };
3533
3534 if let Some(InlaySplice {
3535 to_remove,
3536 to_insert,
3537 }) = self.inlay_hint_cache.spawn_hint_refresh(
3538 reason_description,
3539 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3540 invalidate_cache,
3541 ignore_debounce,
3542 cx,
3543 ) {
3544 self.splice_inlays(to_remove, to_insert, cx);
3545 }
3546 }
3547
3548 fn visible_inlay_hints(&self, cx: &ViewContext<Editor>) -> Vec<Inlay> {
3549 self.display_map
3550 .read(cx)
3551 .current_inlays()
3552 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3553 .cloned()
3554 .collect()
3555 }
3556
3557 pub fn excerpts_for_inlay_hints_query(
3558 &self,
3559 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3560 cx: &mut ViewContext<Editor>,
3561 ) -> HashMap<ExcerptId, (Model<Buffer>, clock::Global, Range<usize>)> {
3562 let Some(project) = self.project.as_ref() else {
3563 return HashMap::default();
3564 };
3565 let project = project.read(cx);
3566 let multi_buffer = self.buffer().read(cx);
3567 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3568 let multi_buffer_visible_start = self
3569 .scroll_manager
3570 .anchor()
3571 .anchor
3572 .to_point(&multi_buffer_snapshot);
3573 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3574 multi_buffer_visible_start
3575 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3576 Bias::Left,
3577 );
3578 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3579 multi_buffer_snapshot
3580 .range_to_buffer_ranges(multi_buffer_visible_range)
3581 .into_iter()
3582 .filter(|(_, excerpt_visible_range)| !excerpt_visible_range.is_empty())
3583 .filter_map(|(excerpt, excerpt_visible_range)| {
3584 let buffer_file = project::File::from_dyn(excerpt.buffer().file())?;
3585 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3586 let worktree_entry = buffer_worktree
3587 .read(cx)
3588 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3589 if worktree_entry.is_ignored {
3590 return None;
3591 }
3592
3593 let language = excerpt.buffer().language()?;
3594 if let Some(restrict_to_languages) = restrict_to_languages {
3595 if !restrict_to_languages.contains(language) {
3596 return None;
3597 }
3598 }
3599 Some((
3600 excerpt.id(),
3601 (
3602 multi_buffer.buffer(excerpt.buffer_id()).unwrap(),
3603 excerpt.buffer().version().clone(),
3604 excerpt_visible_range,
3605 ),
3606 ))
3607 })
3608 .collect()
3609 }
3610
3611 pub fn text_layout_details(&self, cx: &WindowContext) -> TextLayoutDetails {
3612 TextLayoutDetails {
3613 text_system: cx.text_system().clone(),
3614 editor_style: self.style.clone().unwrap(),
3615 rem_size: cx.rem_size(),
3616 scroll_anchor: self.scroll_manager.anchor(),
3617 visible_rows: self.visible_line_count(),
3618 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3619 }
3620 }
3621
3622 pub fn splice_inlays(
3623 &self,
3624 to_remove: Vec<InlayId>,
3625 to_insert: Vec<Inlay>,
3626 cx: &mut ViewContext<Self>,
3627 ) {
3628 self.display_map.update(cx, |display_map, cx| {
3629 display_map.splice_inlays(to_remove, to_insert, cx)
3630 });
3631 cx.notify();
3632 }
3633
3634 fn trigger_on_type_formatting(
3635 &self,
3636 input: String,
3637 cx: &mut ViewContext<Self>,
3638 ) -> Option<Task<Result<()>>> {
3639 if input.len() != 1 {
3640 return None;
3641 }
3642
3643 let project = self.project.as_ref()?;
3644 let position = self.selections.newest_anchor().head();
3645 let (buffer, buffer_position) = self
3646 .buffer
3647 .read(cx)
3648 .text_anchor_for_position(position, cx)?;
3649
3650 let settings = language_settings::language_settings(
3651 buffer
3652 .read(cx)
3653 .language_at(buffer_position)
3654 .map(|l| l.name()),
3655 buffer.read(cx).file(),
3656 cx,
3657 );
3658 if !settings.use_on_type_format {
3659 return None;
3660 }
3661
3662 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3663 // hence we do LSP request & edit on host side only — add formats to host's history.
3664 let push_to_lsp_host_history = true;
3665 // If this is not the host, append its history with new edits.
3666 let push_to_client_history = project.read(cx).is_via_collab();
3667
3668 let on_type_formatting = project.update(cx, |project, cx| {
3669 project.on_type_format(
3670 buffer.clone(),
3671 buffer_position,
3672 input,
3673 push_to_lsp_host_history,
3674 cx,
3675 )
3676 });
3677 Some(cx.spawn(|editor, mut cx| async move {
3678 if let Some(transaction) = on_type_formatting.await? {
3679 if push_to_client_history {
3680 buffer
3681 .update(&mut cx, |buffer, _| {
3682 buffer.push_transaction(transaction, Instant::now());
3683 })
3684 .ok();
3685 }
3686 editor.update(&mut cx, |editor, cx| {
3687 editor.refresh_document_highlights(cx);
3688 })?;
3689 }
3690 Ok(())
3691 }))
3692 }
3693
3694 pub fn show_completions(&mut self, options: &ShowCompletions, cx: &mut ViewContext<Self>) {
3695 if self.pending_rename.is_some() {
3696 return;
3697 }
3698
3699 let Some(provider) = self.completion_provider.as_ref() else {
3700 return;
3701 };
3702
3703 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
3704 return;
3705 }
3706
3707 let position = self.selections.newest_anchor().head();
3708 let (buffer, buffer_position) =
3709 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
3710 output
3711 } else {
3712 return;
3713 };
3714 let show_completion_documentation = buffer
3715 .read(cx)
3716 .snapshot()
3717 .settings_at(buffer_position, cx)
3718 .show_completion_documentation;
3719
3720 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
3721
3722 let trigger_kind = match &options.trigger {
3723 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
3724 CompletionTriggerKind::TRIGGER_CHARACTER
3725 }
3726 _ => CompletionTriggerKind::INVOKED,
3727 };
3728 let completion_context = CompletionContext {
3729 trigger_character: options.trigger.as_ref().and_then(|trigger| {
3730 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
3731 Some(String::from(trigger))
3732 } else {
3733 None
3734 }
3735 }),
3736 trigger_kind,
3737 };
3738 let completions = provider.completions(&buffer, buffer_position, completion_context, cx);
3739 let sort_completions = provider.sort_completions();
3740
3741 let id = post_inc(&mut self.next_completion_id);
3742 let task = cx.spawn(|editor, mut cx| {
3743 async move {
3744 editor.update(&mut cx, |this, _| {
3745 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
3746 })?;
3747 let completions = completions.await.log_err();
3748 let menu = if let Some(completions) = completions {
3749 let mut menu = CompletionsMenu::new(
3750 id,
3751 sort_completions,
3752 show_completion_documentation,
3753 position,
3754 buffer.clone(),
3755 completions.into(),
3756 );
3757
3758 menu.filter(query.as_deref(), cx.background_executor().clone())
3759 .await;
3760
3761 menu.visible().then_some(menu)
3762 } else {
3763 None
3764 };
3765
3766 editor.update(&mut cx, |editor, cx| {
3767 match editor.context_menu.borrow().as_ref() {
3768 None => {}
3769 Some(CodeContextMenu::Completions(prev_menu)) => {
3770 if prev_menu.id > id {
3771 return;
3772 }
3773 }
3774 _ => return,
3775 }
3776
3777 if editor.focus_handle.is_focused(cx) && menu.is_some() {
3778 let mut menu = menu.unwrap();
3779 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
3780
3781 if editor.show_inline_completions_in_menu(cx) {
3782 if let Some(hint) = editor.inline_completion_menu_hint(cx) {
3783 menu.show_inline_completion_hint(hint);
3784 }
3785 } else {
3786 editor.discard_inline_completion(false, cx);
3787 }
3788
3789 *editor.context_menu.borrow_mut() =
3790 Some(CodeContextMenu::Completions(menu));
3791
3792 cx.notify();
3793 } else if editor.completion_tasks.len() <= 1 {
3794 // If there are no more completion tasks and the last menu was
3795 // empty, we should hide it.
3796 let was_hidden = editor.hide_context_menu(cx).is_none();
3797 // If it was already hidden and we don't show inline
3798 // completions in the menu, we should also show the
3799 // inline-completion when available.
3800 if was_hidden && editor.show_inline_completions_in_menu(cx) {
3801 editor.update_visible_inline_completion(cx);
3802 }
3803 }
3804 })?;
3805
3806 Ok::<_, anyhow::Error>(())
3807 }
3808 .log_err()
3809 });
3810
3811 self.completion_tasks.push((id, task));
3812 }
3813
3814 pub fn confirm_completion(
3815 &mut self,
3816 action: &ConfirmCompletion,
3817 cx: &mut ViewContext<Self>,
3818 ) -> Option<Task<Result<()>>> {
3819 self.do_completion(action.item_ix, CompletionIntent::Complete, cx)
3820 }
3821
3822 pub fn compose_completion(
3823 &mut self,
3824 action: &ComposeCompletion,
3825 cx: &mut ViewContext<Self>,
3826 ) -> Option<Task<Result<()>>> {
3827 self.do_completion(action.item_ix, CompletionIntent::Compose, cx)
3828 }
3829
3830 fn do_completion(
3831 &mut self,
3832 item_ix: Option<usize>,
3833 intent: CompletionIntent,
3834 cx: &mut ViewContext<Editor>,
3835 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
3836 use language::ToOffset as _;
3837
3838 {
3839 let context_menu = self.context_menu.borrow();
3840 if let CodeContextMenu::Completions(menu) = context_menu.as_ref()? {
3841 let entries = menu.entries.borrow();
3842 let entry = entries.get(item_ix.unwrap_or(menu.selected_item));
3843 match entry {
3844 Some(CompletionEntry::InlineCompletionHint(
3845 InlineCompletionMenuHint::Loading,
3846 )) => return Some(Task::ready(Ok(()))),
3847 Some(CompletionEntry::InlineCompletionHint(InlineCompletionMenuHint::None)) => {
3848 drop(entries);
3849 drop(context_menu);
3850 self.context_menu_next(&Default::default(), cx);
3851 return Some(Task::ready(Ok(())));
3852 }
3853 _ => {}
3854 }
3855 }
3856 }
3857
3858 let completions_menu =
3859 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3860 menu
3861 } else {
3862 return None;
3863 };
3864
3865 let entries = completions_menu.entries.borrow();
3866 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
3867 let mat = match mat {
3868 CompletionEntry::InlineCompletionHint(_) => {
3869 self.accept_inline_completion(&AcceptInlineCompletion, cx);
3870 cx.stop_propagation();
3871 return Some(Task::ready(Ok(())));
3872 }
3873 CompletionEntry::Match(mat) => {
3874 if self.show_inline_completions_in_menu(cx) {
3875 self.discard_inline_completion(true, cx);
3876 }
3877 mat
3878 }
3879 };
3880 let candidate_id = mat.candidate_id;
3881 drop(entries);
3882
3883 let buffer_handle = completions_menu.buffer;
3884 let completion = completions_menu
3885 .completions
3886 .borrow()
3887 .get(candidate_id)?
3888 .clone();
3889 cx.stop_propagation();
3890
3891 let snippet;
3892 let text;
3893
3894 if completion.is_snippet() {
3895 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3896 text = snippet.as_ref().unwrap().text.clone();
3897 } else {
3898 snippet = None;
3899 text = completion.new_text.clone();
3900 };
3901 let selections = self.selections.all::<usize>(cx);
3902 let buffer = buffer_handle.read(cx);
3903 let old_range = completion.old_range.to_offset(buffer);
3904 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3905
3906 let newest_selection = self.selections.newest_anchor();
3907 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3908 return None;
3909 }
3910
3911 let lookbehind = newest_selection
3912 .start
3913 .text_anchor
3914 .to_offset(buffer)
3915 .saturating_sub(old_range.start);
3916 let lookahead = old_range
3917 .end
3918 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3919 let mut common_prefix_len = old_text
3920 .bytes()
3921 .zip(text.bytes())
3922 .take_while(|(a, b)| a == b)
3923 .count();
3924
3925 let snapshot = self.buffer.read(cx).snapshot(cx);
3926 let mut range_to_replace: Option<Range<isize>> = None;
3927 let mut ranges = Vec::new();
3928 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3929 for selection in &selections {
3930 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3931 let start = selection.start.saturating_sub(lookbehind);
3932 let end = selection.end + lookahead;
3933 if selection.id == newest_selection.id {
3934 range_to_replace = Some(
3935 ((start + common_prefix_len) as isize - selection.start as isize)
3936 ..(end as isize - selection.start as isize),
3937 );
3938 }
3939 ranges.push(start + common_prefix_len..end);
3940 } else {
3941 common_prefix_len = 0;
3942 ranges.clear();
3943 ranges.extend(selections.iter().map(|s| {
3944 if s.id == newest_selection.id {
3945 range_to_replace = Some(
3946 old_range.start.to_offset_utf16(&snapshot).0 as isize
3947 - selection.start as isize
3948 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
3949 - selection.start as isize,
3950 );
3951 old_range.clone()
3952 } else {
3953 s.start..s.end
3954 }
3955 }));
3956 break;
3957 }
3958 if !self.linked_edit_ranges.is_empty() {
3959 let start_anchor = snapshot.anchor_before(selection.head());
3960 let end_anchor = snapshot.anchor_after(selection.tail());
3961 if let Some(ranges) = self
3962 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
3963 {
3964 for (buffer, edits) in ranges {
3965 linked_edits.entry(buffer.clone()).or_default().extend(
3966 edits
3967 .into_iter()
3968 .map(|range| (range, text[common_prefix_len..].to_owned())),
3969 );
3970 }
3971 }
3972 }
3973 }
3974 let text = &text[common_prefix_len..];
3975
3976 cx.emit(EditorEvent::InputHandled {
3977 utf16_range_to_replace: range_to_replace,
3978 text: text.into(),
3979 });
3980
3981 self.transact(cx, |this, cx| {
3982 if let Some(mut snippet) = snippet {
3983 snippet.text = text.to_string();
3984 for tabstop in snippet
3985 .tabstops
3986 .iter_mut()
3987 .flat_map(|tabstop| tabstop.ranges.iter_mut())
3988 {
3989 tabstop.start -= common_prefix_len as isize;
3990 tabstop.end -= common_prefix_len as isize;
3991 }
3992
3993 this.insert_snippet(&ranges, snippet, cx).log_err();
3994 } else {
3995 this.buffer.update(cx, |buffer, cx| {
3996 buffer.edit(
3997 ranges.iter().map(|range| (range.clone(), text)),
3998 this.autoindent_mode.clone(),
3999 cx,
4000 );
4001 });
4002 }
4003 for (buffer, edits) in linked_edits {
4004 buffer.update(cx, |buffer, cx| {
4005 let snapshot = buffer.snapshot();
4006 let edits = edits
4007 .into_iter()
4008 .map(|(range, text)| {
4009 use text::ToPoint as TP;
4010 let end_point = TP::to_point(&range.end, &snapshot);
4011 let start_point = TP::to_point(&range.start, &snapshot);
4012 (start_point..end_point, text)
4013 })
4014 .sorted_by_key(|(range, _)| range.start)
4015 .collect::<Vec<_>>();
4016 buffer.edit(edits, None, cx);
4017 })
4018 }
4019
4020 this.refresh_inline_completion(true, false, cx);
4021 });
4022
4023 let show_new_completions_on_confirm = completion
4024 .confirm
4025 .as_ref()
4026 .map_or(false, |confirm| confirm(intent, cx));
4027 if show_new_completions_on_confirm {
4028 self.show_completions(&ShowCompletions { trigger: None }, cx);
4029 }
4030
4031 let provider = self.completion_provider.as_ref()?;
4032 drop(completion);
4033 let apply_edits = provider.apply_additional_edits_for_completion(
4034 buffer_handle,
4035 completions_menu.completions.clone(),
4036 candidate_id,
4037 true,
4038 cx,
4039 );
4040
4041 let editor_settings = EditorSettings::get_global(cx);
4042 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4043 // After the code completion is finished, users often want to know what signatures are needed.
4044 // so we should automatically call signature_help
4045 self.show_signature_help(&ShowSignatureHelp, cx);
4046 }
4047
4048 Some(cx.foreground_executor().spawn(async move {
4049 apply_edits.await?;
4050 Ok(())
4051 }))
4052 }
4053
4054 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
4055 let mut context_menu = self.context_menu.borrow_mut();
4056 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4057 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4058 // Toggle if we're selecting the same one
4059 *context_menu = None;
4060 cx.notify();
4061 return;
4062 } else {
4063 // Otherwise, clear it and start a new one
4064 *context_menu = None;
4065 cx.notify();
4066 }
4067 }
4068 drop(context_menu);
4069 let snapshot = self.snapshot(cx);
4070 let deployed_from_indicator = action.deployed_from_indicator;
4071 let mut task = self.code_actions_task.take();
4072 let action = action.clone();
4073 cx.spawn(|editor, mut cx| async move {
4074 while let Some(prev_task) = task {
4075 prev_task.await.log_err();
4076 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4077 }
4078
4079 let spawned_test_task = editor.update(&mut cx, |editor, cx| {
4080 if editor.focus_handle.is_focused(cx) {
4081 let multibuffer_point = action
4082 .deployed_from_indicator
4083 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4084 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4085 let (buffer, buffer_row) = snapshot
4086 .buffer_snapshot
4087 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4088 .and_then(|(buffer_snapshot, range)| {
4089 editor
4090 .buffer
4091 .read(cx)
4092 .buffer(buffer_snapshot.remote_id())
4093 .map(|buffer| (buffer, range.start.row))
4094 })?;
4095 let (_, code_actions) = editor
4096 .available_code_actions
4097 .clone()
4098 .and_then(|(location, code_actions)| {
4099 let snapshot = location.buffer.read(cx).snapshot();
4100 let point_range = location.range.to_point(&snapshot);
4101 let point_range = point_range.start.row..=point_range.end.row;
4102 if point_range.contains(&buffer_row) {
4103 Some((location, code_actions))
4104 } else {
4105 None
4106 }
4107 })
4108 .unzip();
4109 let buffer_id = buffer.read(cx).remote_id();
4110 let tasks = editor
4111 .tasks
4112 .get(&(buffer_id, buffer_row))
4113 .map(|t| Arc::new(t.to_owned()));
4114 if tasks.is_none() && code_actions.is_none() {
4115 return None;
4116 }
4117
4118 editor.completion_tasks.clear();
4119 editor.discard_inline_completion(false, cx);
4120 let task_context =
4121 tasks
4122 .as_ref()
4123 .zip(editor.project.clone())
4124 .map(|(tasks, project)| {
4125 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4126 });
4127
4128 Some(cx.spawn(|editor, mut cx| async move {
4129 let task_context = match task_context {
4130 Some(task_context) => task_context.await,
4131 None => None,
4132 };
4133 let resolved_tasks =
4134 tasks.zip(task_context).map(|(tasks, task_context)| {
4135 Rc::new(ResolvedTasks {
4136 templates: tasks.resolve(&task_context).collect(),
4137 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4138 multibuffer_point.row,
4139 tasks.column,
4140 )),
4141 })
4142 });
4143 let spawn_straight_away = resolved_tasks
4144 .as_ref()
4145 .map_or(false, |tasks| tasks.templates.len() == 1)
4146 && code_actions
4147 .as_ref()
4148 .map_or(true, |actions| actions.is_empty());
4149 if let Ok(task) = editor.update(&mut cx, |editor, cx| {
4150 *editor.context_menu.borrow_mut() =
4151 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4152 buffer,
4153 actions: CodeActionContents {
4154 tasks: resolved_tasks,
4155 actions: code_actions,
4156 },
4157 selected_item: Default::default(),
4158 scroll_handle: UniformListScrollHandle::default(),
4159 deployed_from_indicator,
4160 }));
4161 if spawn_straight_away {
4162 if let Some(task) = editor.confirm_code_action(
4163 &ConfirmCodeAction { item_ix: Some(0) },
4164 cx,
4165 ) {
4166 cx.notify();
4167 return task;
4168 }
4169 }
4170 cx.notify();
4171 Task::ready(Ok(()))
4172 }) {
4173 task.await
4174 } else {
4175 Ok(())
4176 }
4177 }))
4178 } else {
4179 Some(Task::ready(Ok(())))
4180 }
4181 })?;
4182 if let Some(task) = spawned_test_task {
4183 task.await?;
4184 }
4185
4186 Ok::<_, anyhow::Error>(())
4187 })
4188 .detach_and_log_err(cx);
4189 }
4190
4191 pub fn confirm_code_action(
4192 &mut self,
4193 action: &ConfirmCodeAction,
4194 cx: &mut ViewContext<Self>,
4195 ) -> Option<Task<Result<()>>> {
4196 let actions_menu = if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
4197 menu
4198 } else {
4199 return None;
4200 };
4201 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4202 let action = actions_menu.actions.get(action_ix)?;
4203 let title = action.label();
4204 let buffer = actions_menu.buffer;
4205 let workspace = self.workspace()?;
4206
4207 match action {
4208 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4209 workspace.update(cx, |workspace, cx| {
4210 workspace::tasks::schedule_resolved_task(
4211 workspace,
4212 task_source_kind,
4213 resolved_task,
4214 false,
4215 cx,
4216 );
4217
4218 Some(Task::ready(Ok(())))
4219 })
4220 }
4221 CodeActionsItem::CodeAction {
4222 excerpt_id,
4223 action,
4224 provider,
4225 } => {
4226 let apply_code_action =
4227 provider.apply_code_action(buffer, action, excerpt_id, true, cx);
4228 let workspace = workspace.downgrade();
4229 Some(cx.spawn(|editor, cx| async move {
4230 let project_transaction = apply_code_action.await?;
4231 Self::open_project_transaction(
4232 &editor,
4233 workspace,
4234 project_transaction,
4235 title,
4236 cx,
4237 )
4238 .await
4239 }))
4240 }
4241 }
4242 }
4243
4244 pub async fn open_project_transaction(
4245 this: &WeakView<Editor>,
4246 workspace: WeakView<Workspace>,
4247 transaction: ProjectTransaction,
4248 title: String,
4249 mut cx: AsyncWindowContext,
4250 ) -> Result<()> {
4251 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4252 cx.update(|cx| {
4253 entries.sort_unstable_by_key(|(buffer, _)| {
4254 buffer.read(cx).file().map(|f| f.path().clone())
4255 });
4256 })?;
4257
4258 // If the project transaction's edits are all contained within this editor, then
4259 // avoid opening a new editor to display them.
4260
4261 if let Some((buffer, transaction)) = entries.first() {
4262 if entries.len() == 1 {
4263 let excerpt = this.update(&mut cx, |editor, cx| {
4264 editor
4265 .buffer()
4266 .read(cx)
4267 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4268 })?;
4269 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4270 if excerpted_buffer == *buffer {
4271 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4272 let excerpt_range = excerpt_range.to_offset(buffer);
4273 buffer
4274 .edited_ranges_for_transaction::<usize>(transaction)
4275 .all(|range| {
4276 excerpt_range.start <= range.start
4277 && excerpt_range.end >= range.end
4278 })
4279 })?;
4280
4281 if all_edits_within_excerpt {
4282 return Ok(());
4283 }
4284 }
4285 }
4286 }
4287 } else {
4288 return Ok(());
4289 }
4290
4291 let mut ranges_to_highlight = Vec::new();
4292 let excerpt_buffer = cx.new_model(|cx| {
4293 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4294 for (buffer_handle, transaction) in &entries {
4295 let buffer = buffer_handle.read(cx);
4296 ranges_to_highlight.extend(
4297 multibuffer.push_excerpts_with_context_lines(
4298 buffer_handle.clone(),
4299 buffer
4300 .edited_ranges_for_transaction::<usize>(transaction)
4301 .collect(),
4302 DEFAULT_MULTIBUFFER_CONTEXT,
4303 cx,
4304 ),
4305 );
4306 }
4307 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4308 multibuffer
4309 })?;
4310
4311 workspace.update(&mut cx, |workspace, cx| {
4312 let project = workspace.project().clone();
4313 let editor =
4314 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), true, cx));
4315 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
4316 editor.update(cx, |editor, cx| {
4317 editor.highlight_background::<Self>(
4318 &ranges_to_highlight,
4319 |theme| theme.editor_highlighted_line_background,
4320 cx,
4321 );
4322 });
4323 })?;
4324
4325 Ok(())
4326 }
4327
4328 pub fn clear_code_action_providers(&mut self) {
4329 self.code_action_providers.clear();
4330 self.available_code_actions.take();
4331 }
4332
4333 pub fn add_code_action_provider(
4334 &mut self,
4335 provider: Rc<dyn CodeActionProvider>,
4336 cx: &mut ViewContext<Self>,
4337 ) {
4338 if self
4339 .code_action_providers
4340 .iter()
4341 .any(|existing_provider| existing_provider.id() == provider.id())
4342 {
4343 return;
4344 }
4345
4346 self.code_action_providers.push(provider);
4347 self.refresh_code_actions(cx);
4348 }
4349
4350 pub fn remove_code_action_provider(&mut self, id: Arc<str>, cx: &mut ViewContext<Self>) {
4351 self.code_action_providers
4352 .retain(|provider| provider.id() != id);
4353 self.refresh_code_actions(cx);
4354 }
4355
4356 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4357 let buffer = self.buffer.read(cx);
4358 let newest_selection = self.selections.newest_anchor().clone();
4359 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4360 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4361 if start_buffer != end_buffer {
4362 return None;
4363 }
4364
4365 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
4366 cx.background_executor()
4367 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4368 .await;
4369
4370 let (providers, tasks) = this.update(&mut cx, |this, cx| {
4371 let providers = this.code_action_providers.clone();
4372 let tasks = this
4373 .code_action_providers
4374 .iter()
4375 .map(|provider| provider.code_actions(&start_buffer, start..end, cx))
4376 .collect::<Vec<_>>();
4377 (providers, tasks)
4378 })?;
4379
4380 let mut actions = Vec::new();
4381 for (provider, provider_actions) in
4382 providers.into_iter().zip(future::join_all(tasks).await)
4383 {
4384 if let Some(provider_actions) = provider_actions.log_err() {
4385 actions.extend(provider_actions.into_iter().map(|action| {
4386 AvailableCodeAction {
4387 excerpt_id: newest_selection.start.excerpt_id,
4388 action,
4389 provider: provider.clone(),
4390 }
4391 }));
4392 }
4393 }
4394
4395 this.update(&mut cx, |this, cx| {
4396 this.available_code_actions = if actions.is_empty() {
4397 None
4398 } else {
4399 Some((
4400 Location {
4401 buffer: start_buffer,
4402 range: start..end,
4403 },
4404 actions.into(),
4405 ))
4406 };
4407 cx.notify();
4408 })
4409 }));
4410 None
4411 }
4412
4413 fn start_inline_blame_timer(&mut self, cx: &mut ViewContext<Self>) {
4414 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4415 self.show_git_blame_inline = false;
4416
4417 self.show_git_blame_inline_delay_task = Some(cx.spawn(|this, mut cx| async move {
4418 cx.background_executor().timer(delay).await;
4419
4420 this.update(&mut cx, |this, cx| {
4421 this.show_git_blame_inline = true;
4422 cx.notify();
4423 })
4424 .log_err();
4425 }));
4426 }
4427 }
4428
4429 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4430 if self.pending_rename.is_some() {
4431 return None;
4432 }
4433
4434 let provider = self.semantics_provider.clone()?;
4435 let buffer = self.buffer.read(cx);
4436 let newest_selection = self.selections.newest_anchor().clone();
4437 let cursor_position = newest_selection.head();
4438 let (cursor_buffer, cursor_buffer_position) =
4439 buffer.text_anchor_for_position(cursor_position, cx)?;
4440 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4441 if cursor_buffer != tail_buffer {
4442 return None;
4443 }
4444 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4445 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4446 cx.background_executor()
4447 .timer(Duration::from_millis(debounce))
4448 .await;
4449
4450 let highlights = if let Some(highlights) = cx
4451 .update(|cx| {
4452 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4453 })
4454 .ok()
4455 .flatten()
4456 {
4457 highlights.await.log_err()
4458 } else {
4459 None
4460 };
4461
4462 if let Some(highlights) = highlights {
4463 this.update(&mut cx, |this, cx| {
4464 if this.pending_rename.is_some() {
4465 return;
4466 }
4467
4468 let buffer_id = cursor_position.buffer_id;
4469 let buffer = this.buffer.read(cx);
4470 if !buffer
4471 .text_anchor_for_position(cursor_position, cx)
4472 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4473 {
4474 return;
4475 }
4476
4477 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4478 let mut write_ranges = Vec::new();
4479 let mut read_ranges = Vec::new();
4480 for highlight in highlights {
4481 for (excerpt_id, excerpt_range) in
4482 buffer.excerpts_for_buffer(&cursor_buffer, cx)
4483 {
4484 let start = highlight
4485 .range
4486 .start
4487 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4488 let end = highlight
4489 .range
4490 .end
4491 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4492 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4493 continue;
4494 }
4495
4496 let range = Anchor {
4497 buffer_id,
4498 excerpt_id,
4499 text_anchor: start,
4500 }..Anchor {
4501 buffer_id,
4502 excerpt_id,
4503 text_anchor: end,
4504 };
4505 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4506 write_ranges.push(range);
4507 } else {
4508 read_ranges.push(range);
4509 }
4510 }
4511 }
4512
4513 this.highlight_background::<DocumentHighlightRead>(
4514 &read_ranges,
4515 |theme| theme.editor_document_highlight_read_background,
4516 cx,
4517 );
4518 this.highlight_background::<DocumentHighlightWrite>(
4519 &write_ranges,
4520 |theme| theme.editor_document_highlight_write_background,
4521 cx,
4522 );
4523 cx.notify();
4524 })
4525 .log_err();
4526 }
4527 }));
4528 None
4529 }
4530
4531 pub fn refresh_inline_completion(
4532 &mut self,
4533 debounce: bool,
4534 user_requested: bool,
4535 cx: &mut ViewContext<Self>,
4536 ) -> Option<()> {
4537 let provider = self.inline_completion_provider()?;
4538 let cursor = self.selections.newest_anchor().head();
4539 let (buffer, cursor_buffer_position) =
4540 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4541
4542 if !user_requested
4543 && (!self.enable_inline_completions
4544 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4545 || !self.is_focused(cx)
4546 || buffer.read(cx).is_empty())
4547 {
4548 self.discard_inline_completion(false, cx);
4549 return None;
4550 }
4551
4552 self.update_visible_inline_completion(cx);
4553 provider.refresh(buffer, cursor_buffer_position, debounce, cx);
4554 Some(())
4555 }
4556
4557 fn cycle_inline_completion(
4558 &mut self,
4559 direction: Direction,
4560 cx: &mut ViewContext<Self>,
4561 ) -> Option<()> {
4562 let provider = self.inline_completion_provider()?;
4563 let cursor = self.selections.newest_anchor().head();
4564 let (buffer, cursor_buffer_position) =
4565 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4566 if !self.enable_inline_completions
4567 || !self.should_show_inline_completions(&buffer, cursor_buffer_position, cx)
4568 {
4569 return None;
4570 }
4571
4572 provider.cycle(buffer, cursor_buffer_position, direction, cx);
4573 self.update_visible_inline_completion(cx);
4574
4575 Some(())
4576 }
4577
4578 pub fn show_inline_completion(&mut self, _: &ShowInlineCompletion, cx: &mut ViewContext<Self>) {
4579 if !self.has_active_inline_completion() {
4580 self.refresh_inline_completion(false, true, cx);
4581 return;
4582 }
4583
4584 self.update_visible_inline_completion(cx);
4585 }
4586
4587 pub fn display_cursor_names(&mut self, _: &DisplayCursorNames, cx: &mut ViewContext<Self>) {
4588 self.show_cursor_names(cx);
4589 }
4590
4591 fn show_cursor_names(&mut self, cx: &mut ViewContext<Self>) {
4592 self.show_cursor_names = true;
4593 cx.notify();
4594 cx.spawn(|this, mut cx| async move {
4595 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
4596 this.update(&mut cx, |this, cx| {
4597 this.show_cursor_names = false;
4598 cx.notify()
4599 })
4600 .ok()
4601 })
4602 .detach();
4603 }
4604
4605 pub fn next_inline_completion(&mut self, _: &NextInlineCompletion, cx: &mut ViewContext<Self>) {
4606 if self.has_active_inline_completion() {
4607 self.cycle_inline_completion(Direction::Next, cx);
4608 } else {
4609 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4610 if is_copilot_disabled {
4611 cx.propagate();
4612 }
4613 }
4614 }
4615
4616 pub fn previous_inline_completion(
4617 &mut self,
4618 _: &PreviousInlineCompletion,
4619 cx: &mut ViewContext<Self>,
4620 ) {
4621 if self.has_active_inline_completion() {
4622 self.cycle_inline_completion(Direction::Prev, cx);
4623 } else {
4624 let is_copilot_disabled = self.refresh_inline_completion(false, true, cx).is_none();
4625 if is_copilot_disabled {
4626 cx.propagate();
4627 }
4628 }
4629 }
4630
4631 pub fn accept_inline_completion(
4632 &mut self,
4633 _: &AcceptInlineCompletion,
4634 cx: &mut ViewContext<Self>,
4635 ) {
4636 let buffer = self.buffer.read(cx);
4637 let snapshot = buffer.snapshot(cx);
4638 let selection = self.selections.newest_adjusted(cx);
4639 let cursor = selection.head();
4640 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
4641 let suggested_indents = snapshot.suggested_indents([cursor.row], cx);
4642 if let Some(suggested_indent) = suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
4643 {
4644 if cursor.column < suggested_indent.len
4645 && cursor.column <= current_indent.len
4646 && current_indent.len <= suggested_indent.len
4647 {
4648 self.tab(&Default::default(), cx);
4649 return;
4650 }
4651 }
4652
4653 if self.show_inline_completions_in_menu(cx) {
4654 self.hide_context_menu(cx);
4655 }
4656
4657 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4658 return;
4659 };
4660
4661 self.report_inline_completion_event(true, cx);
4662
4663 match &active_inline_completion.completion {
4664 InlineCompletion::Move(position) => {
4665 let position = *position;
4666 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4667 selections.select_anchor_ranges([position..position]);
4668 });
4669 }
4670 InlineCompletion::Edit(edits) => {
4671 if let Some(provider) = self.inline_completion_provider() {
4672 provider.accept(cx);
4673 }
4674
4675 let snapshot = self.buffer.read(cx).snapshot(cx);
4676 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
4677
4678 self.buffer.update(cx, |buffer, cx| {
4679 buffer.edit(edits.iter().cloned(), None, cx)
4680 });
4681
4682 self.change_selections(None, cx, |s| {
4683 s.select_anchor_ranges([last_edit_end..last_edit_end])
4684 });
4685
4686 self.update_visible_inline_completion(cx);
4687 if self.active_inline_completion.is_none() {
4688 self.refresh_inline_completion(true, true, cx);
4689 }
4690
4691 cx.notify();
4692 }
4693 }
4694 }
4695
4696 pub fn accept_partial_inline_completion(
4697 &mut self,
4698 _: &AcceptPartialInlineCompletion,
4699 cx: &mut ViewContext<Self>,
4700 ) {
4701 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
4702 return;
4703 };
4704 if self.selections.count() != 1 {
4705 return;
4706 }
4707
4708 self.report_inline_completion_event(true, cx);
4709
4710 match &active_inline_completion.completion {
4711 InlineCompletion::Move(position) => {
4712 let position = *position;
4713 self.change_selections(Some(Autoscroll::newest()), cx, |selections| {
4714 selections.select_anchor_ranges([position..position]);
4715 });
4716 }
4717 InlineCompletion::Edit(edits) => {
4718 // Find an insertion that starts at the cursor position.
4719 let snapshot = self.buffer.read(cx).snapshot(cx);
4720 let cursor_offset = self.selections.newest::<usize>(cx).head();
4721 let insertion = edits.iter().find_map(|(range, text)| {
4722 let range = range.to_offset(&snapshot);
4723 if range.is_empty() && range.start == cursor_offset {
4724 Some(text)
4725 } else {
4726 None
4727 }
4728 });
4729
4730 if let Some(text) = insertion {
4731 let mut partial_completion = text
4732 .chars()
4733 .by_ref()
4734 .take_while(|c| c.is_alphabetic())
4735 .collect::<String>();
4736 if partial_completion.is_empty() {
4737 partial_completion = text
4738 .chars()
4739 .by_ref()
4740 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
4741 .collect::<String>();
4742 }
4743
4744 cx.emit(EditorEvent::InputHandled {
4745 utf16_range_to_replace: None,
4746 text: partial_completion.clone().into(),
4747 });
4748
4749 self.insert_with_autoindent_mode(&partial_completion, None, cx);
4750
4751 self.refresh_inline_completion(true, true, cx);
4752 cx.notify();
4753 } else {
4754 self.accept_inline_completion(&Default::default(), cx);
4755 }
4756 }
4757 }
4758 }
4759
4760 fn discard_inline_completion(
4761 &mut self,
4762 should_report_inline_completion_event: bool,
4763 cx: &mut ViewContext<Self>,
4764 ) -> bool {
4765 if should_report_inline_completion_event {
4766 self.report_inline_completion_event(false, cx);
4767 }
4768
4769 if let Some(provider) = self.inline_completion_provider() {
4770 provider.discard(cx);
4771 }
4772
4773 self.take_active_inline_completion(cx).is_some()
4774 }
4775
4776 fn report_inline_completion_event(&self, accepted: bool, cx: &AppContext) {
4777 let Some(provider) = self.inline_completion_provider() else {
4778 return;
4779 };
4780 let Some(project) = self.project.as_ref() else {
4781 return;
4782 };
4783 let Some((_, buffer, _)) = self
4784 .buffer
4785 .read(cx)
4786 .excerpt_containing(self.selections.newest_anchor().head(), cx)
4787 else {
4788 return;
4789 };
4790
4791 let project = project.read(cx);
4792 let extension = buffer
4793 .read(cx)
4794 .file()
4795 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
4796 project.client().telemetry().report_inline_completion_event(
4797 provider.name().into(),
4798 accepted,
4799 extension,
4800 );
4801 }
4802
4803 pub fn has_active_inline_completion(&self) -> bool {
4804 self.active_inline_completion.is_some()
4805 }
4806
4807 fn take_active_inline_completion(
4808 &mut self,
4809 cx: &mut ViewContext<Self>,
4810 ) -> Option<InlineCompletion> {
4811 let active_inline_completion = self.active_inline_completion.take()?;
4812 self.splice_inlays(active_inline_completion.inlay_ids, Default::default(), cx);
4813 self.clear_highlights::<InlineCompletionHighlight>(cx);
4814 Some(active_inline_completion.completion)
4815 }
4816
4817 fn update_visible_inline_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
4818 let selection = self.selections.newest_anchor();
4819 let cursor = selection.head();
4820 let multibuffer = self.buffer.read(cx).snapshot(cx);
4821 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
4822 let excerpt_id = cursor.excerpt_id;
4823
4824 let completions_menu_has_precedence = !self.show_inline_completions_in_menu(cx)
4825 && (self.context_menu.borrow().is_some()
4826 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
4827 if completions_menu_has_precedence
4828 || !offset_selection.is_empty()
4829 || !self.enable_inline_completions
4830 || self
4831 .active_inline_completion
4832 .as_ref()
4833 .map_or(false, |completion| {
4834 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
4835 let invalidation_range = invalidation_range.start..=invalidation_range.end;
4836 !invalidation_range.contains(&offset_selection.head())
4837 })
4838 {
4839 self.discard_inline_completion(false, cx);
4840 return None;
4841 }
4842
4843 self.take_active_inline_completion(cx);
4844 let provider = self.inline_completion_provider()?;
4845
4846 let (buffer, cursor_buffer_position) =
4847 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
4848
4849 let completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
4850 let edits = completion
4851 .edits
4852 .into_iter()
4853 .flat_map(|(range, new_text)| {
4854 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
4855 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
4856 Some((start..end, new_text))
4857 })
4858 .collect::<Vec<_>>();
4859 if edits.is_empty() {
4860 return None;
4861 }
4862
4863 let first_edit_start = edits.first().unwrap().0.start;
4864 let edit_start_row = first_edit_start
4865 .to_point(&multibuffer)
4866 .row
4867 .saturating_sub(2);
4868
4869 let last_edit_end = edits.last().unwrap().0.end;
4870 let edit_end_row = cmp::min(
4871 multibuffer.max_point().row,
4872 last_edit_end.to_point(&multibuffer).row + 2,
4873 );
4874
4875 let cursor_row = cursor.to_point(&multibuffer).row;
4876
4877 let mut inlay_ids = Vec::new();
4878 let invalidation_row_range;
4879 let completion;
4880 if cursor_row < edit_start_row {
4881 invalidation_row_range = cursor_row..edit_end_row;
4882 completion = InlineCompletion::Move(first_edit_start);
4883 } else if cursor_row > edit_end_row {
4884 invalidation_row_range = edit_start_row..cursor_row;
4885 completion = InlineCompletion::Move(first_edit_start);
4886 } else {
4887 if edits
4888 .iter()
4889 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
4890 {
4891 let mut inlays = Vec::new();
4892 for (range, new_text) in &edits {
4893 let inlay = Inlay::inline_completion(
4894 post_inc(&mut self.next_inlay_id),
4895 range.start,
4896 new_text.as_str(),
4897 );
4898 inlay_ids.push(inlay.id);
4899 inlays.push(inlay);
4900 }
4901
4902 self.splice_inlays(vec![], inlays, cx);
4903 } else {
4904 let background_color = cx.theme().status().deleted_background;
4905 self.highlight_text::<InlineCompletionHighlight>(
4906 edits.iter().map(|(range, _)| range.clone()).collect(),
4907 HighlightStyle {
4908 background_color: Some(background_color),
4909 ..Default::default()
4910 },
4911 cx,
4912 );
4913 }
4914
4915 invalidation_row_range = edit_start_row..edit_end_row;
4916 completion = InlineCompletion::Edit(edits);
4917 };
4918
4919 let invalidation_range = multibuffer
4920 .anchor_before(Point::new(invalidation_row_range.start, 0))
4921 ..multibuffer.anchor_after(Point::new(
4922 invalidation_row_range.end,
4923 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
4924 ));
4925
4926 self.active_inline_completion = Some(InlineCompletionState {
4927 inlay_ids,
4928 completion,
4929 invalidation_range,
4930 });
4931
4932 if self.show_inline_completions_in_menu(cx) && self.has_active_completions_menu() {
4933 if let Some(hint) = self.inline_completion_menu_hint(cx) {
4934 match self.context_menu.borrow_mut().as_mut() {
4935 Some(CodeContextMenu::Completions(menu)) => {
4936 menu.show_inline_completion_hint(hint);
4937 }
4938 _ => {}
4939 }
4940 }
4941 }
4942
4943 cx.notify();
4944
4945 Some(())
4946 }
4947
4948 fn inline_completion_menu_hint(
4949 &mut self,
4950 cx: &mut ViewContext<Self>,
4951 ) -> Option<InlineCompletionMenuHint> {
4952 let provider = self.inline_completion_provider()?;
4953 if self.has_active_inline_completion() {
4954 let editor_snapshot = self.snapshot(cx);
4955
4956 let text = match &self.active_inline_completion.as_ref()?.completion {
4957 InlineCompletion::Edit(edits) => {
4958 inline_completion_edit_text(&editor_snapshot, edits, true, cx)
4959 }
4960 InlineCompletion::Move(target) => {
4961 let target_point =
4962 target.to_point(&editor_snapshot.display_snapshot.buffer_snapshot);
4963 let target_line = target_point.row + 1;
4964 InlineCompletionText::Move(
4965 format!("Jump to edit in line {}", target_line).into(),
4966 )
4967 }
4968 };
4969
4970 Some(InlineCompletionMenuHint::Loaded { text })
4971 } else if provider.is_refreshing(cx) {
4972 Some(InlineCompletionMenuHint::Loading)
4973 } else {
4974 Some(InlineCompletionMenuHint::None)
4975 }
4976 }
4977
4978 pub fn inline_completion_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
4979 Some(self.inline_completion_provider.as_ref()?.provider.clone())
4980 }
4981
4982 fn show_inline_completions_in_menu(&self, cx: &AppContext) -> bool {
4983 EditorSettings::get_global(cx).show_inline_completions_in_menu
4984 && self
4985 .inline_completion_provider()
4986 .map_or(false, |provider| provider.show_completions_in_menu())
4987 }
4988
4989 fn render_code_actions_indicator(
4990 &self,
4991 _style: &EditorStyle,
4992 row: DisplayRow,
4993 is_active: bool,
4994 cx: &mut ViewContext<Self>,
4995 ) -> Option<IconButton> {
4996 if self.available_code_actions.is_some() {
4997 Some(
4998 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
4999 .shape(ui::IconButtonShape::Square)
5000 .icon_size(IconSize::XSmall)
5001 .icon_color(Color::Muted)
5002 .toggle_state(is_active)
5003 .tooltip({
5004 let focus_handle = self.focus_handle.clone();
5005 move |cx| {
5006 Tooltip::for_action_in(
5007 "Toggle Code Actions",
5008 &ToggleCodeActions {
5009 deployed_from_indicator: None,
5010 },
5011 &focus_handle,
5012 cx,
5013 )
5014 }
5015 })
5016 .on_click(cx.listener(move |editor, _e, cx| {
5017 editor.focus(cx);
5018 editor.toggle_code_actions(
5019 &ToggleCodeActions {
5020 deployed_from_indicator: Some(row),
5021 },
5022 cx,
5023 );
5024 })),
5025 )
5026 } else {
5027 None
5028 }
5029 }
5030
5031 fn clear_tasks(&mut self) {
5032 self.tasks.clear()
5033 }
5034
5035 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5036 if self.tasks.insert(key, value).is_some() {
5037 // This case should hopefully be rare, but just in case...
5038 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5039 }
5040 }
5041
5042 fn build_tasks_context(
5043 project: &Model<Project>,
5044 buffer: &Model<Buffer>,
5045 buffer_row: u32,
5046 tasks: &Arc<RunnableTasks>,
5047 cx: &mut ViewContext<Self>,
5048 ) -> Task<Option<task::TaskContext>> {
5049 let position = Point::new(buffer_row, tasks.column);
5050 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5051 let location = Location {
5052 buffer: buffer.clone(),
5053 range: range_start..range_start,
5054 };
5055 // Fill in the environmental variables from the tree-sitter captures
5056 let mut captured_task_variables = TaskVariables::default();
5057 for (capture_name, value) in tasks.extra_variables.clone() {
5058 captured_task_variables.insert(
5059 task::VariableName::Custom(capture_name.into()),
5060 value.clone(),
5061 );
5062 }
5063 project.update(cx, |project, cx| {
5064 project.task_store().update(cx, |task_store, cx| {
5065 task_store.task_context_for_location(captured_task_variables, location, cx)
5066 })
5067 })
5068 }
5069
5070 pub fn spawn_nearest_task(&mut self, action: &SpawnNearestTask, cx: &mut ViewContext<Self>) {
5071 let Some((workspace, _)) = self.workspace.clone() else {
5072 return;
5073 };
5074 let Some(project) = self.project.clone() else {
5075 return;
5076 };
5077
5078 // Try to find a closest, enclosing node using tree-sitter that has a
5079 // task
5080 let Some((buffer, buffer_row, tasks)) = self
5081 .find_enclosing_node_task(cx)
5082 // Or find the task that's closest in row-distance.
5083 .or_else(|| self.find_closest_task(cx))
5084 else {
5085 return;
5086 };
5087
5088 let reveal_strategy = action.reveal;
5089 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5090 cx.spawn(|_, mut cx| async move {
5091 let context = task_context.await?;
5092 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5093
5094 let resolved = resolved_task.resolved.as_mut()?;
5095 resolved.reveal = reveal_strategy;
5096
5097 workspace
5098 .update(&mut cx, |workspace, cx| {
5099 workspace::tasks::schedule_resolved_task(
5100 workspace,
5101 task_source_kind,
5102 resolved_task,
5103 false,
5104 cx,
5105 );
5106 })
5107 .ok()
5108 })
5109 .detach();
5110 }
5111
5112 fn find_closest_task(
5113 &mut self,
5114 cx: &mut ViewContext<Self>,
5115 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5116 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5117
5118 let ((buffer_id, row), tasks) = self
5119 .tasks
5120 .iter()
5121 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5122
5123 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5124 let tasks = Arc::new(tasks.to_owned());
5125 Some((buffer, *row, tasks))
5126 }
5127
5128 fn find_enclosing_node_task(
5129 &mut self,
5130 cx: &mut ViewContext<Self>,
5131 ) -> Option<(Model<Buffer>, u32, Arc<RunnableTasks>)> {
5132 let snapshot = self.buffer.read(cx).snapshot(cx);
5133 let offset = self.selections.newest::<usize>(cx).head();
5134 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5135 let buffer_id = excerpt.buffer().remote_id();
5136
5137 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5138 let mut cursor = layer.node().walk();
5139
5140 while cursor.goto_first_child_for_byte(offset).is_some() {
5141 if cursor.node().end_byte() == offset {
5142 cursor.goto_next_sibling();
5143 }
5144 }
5145
5146 // Ascend to the smallest ancestor that contains the range and has a task.
5147 loop {
5148 let node = cursor.node();
5149 let node_range = node.byte_range();
5150 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
5151
5152 // Check if this node contains our offset
5153 if node_range.start <= offset && node_range.end >= offset {
5154 // If it contains offset, check for task
5155 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
5156 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
5157 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
5158 }
5159 }
5160
5161 if !cursor.goto_parent() {
5162 break;
5163 }
5164 }
5165 None
5166 }
5167
5168 fn render_run_indicator(
5169 &self,
5170 _style: &EditorStyle,
5171 is_active: bool,
5172 row: DisplayRow,
5173 cx: &mut ViewContext<Self>,
5174 ) -> IconButton {
5175 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
5176 .shape(ui::IconButtonShape::Square)
5177 .icon_size(IconSize::XSmall)
5178 .icon_color(Color::Muted)
5179 .toggle_state(is_active)
5180 .on_click(cx.listener(move |editor, _e, cx| {
5181 editor.focus(cx);
5182 editor.toggle_code_actions(
5183 &ToggleCodeActions {
5184 deployed_from_indicator: Some(row),
5185 },
5186 cx,
5187 );
5188 }))
5189 }
5190
5191 #[cfg(any(feature = "test-support", test))]
5192 pub fn context_menu_visible(&self) -> bool {
5193 self.context_menu
5194 .borrow()
5195 .as_ref()
5196 .map_or(false, |menu| menu.visible())
5197 }
5198
5199 #[cfg(feature = "test-support")]
5200 pub fn context_menu_contains_inline_completion(&self) -> bool {
5201 self.context_menu
5202 .borrow()
5203 .as_ref()
5204 .map_or(false, |menu| match menu {
5205 CodeContextMenu::Completions(menu) => {
5206 menu.entries.borrow().first().map_or(false, |entry| {
5207 matches!(entry, CompletionEntry::InlineCompletionHint(_))
5208 })
5209 }
5210 CodeContextMenu::CodeActions(_) => false,
5211 })
5212 }
5213
5214 fn context_menu_origin(&self, cursor_position: DisplayPoint) -> Option<ContextMenuOrigin> {
5215 self.context_menu
5216 .borrow()
5217 .as_ref()
5218 .map(|menu| menu.origin(cursor_position))
5219 }
5220
5221 fn render_context_menu(
5222 &self,
5223 style: &EditorStyle,
5224 max_height_in_lines: u32,
5225 cx: &mut ViewContext<Editor>,
5226 ) -> Option<AnyElement> {
5227 self.context_menu.borrow().as_ref().and_then(|menu| {
5228 if menu.visible() {
5229 Some(menu.render(style, max_height_in_lines, cx))
5230 } else {
5231 None
5232 }
5233 })
5234 }
5235
5236 fn render_context_menu_aside(
5237 &self,
5238 style: &EditorStyle,
5239 max_size: Size<Pixels>,
5240 cx: &mut ViewContext<Editor>,
5241 ) -> Option<AnyElement> {
5242 self.context_menu.borrow().as_ref().and_then(|menu| {
5243 if menu.visible() {
5244 menu.render_aside(
5245 style,
5246 max_size,
5247 self.workspace.as_ref().map(|(w, _)| w.clone()),
5248 cx,
5249 )
5250 } else {
5251 None
5252 }
5253 })
5254 }
5255
5256 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<CodeContextMenu> {
5257 cx.notify();
5258 self.completion_tasks.clear();
5259 let context_menu = self.context_menu.borrow_mut().take();
5260 if context_menu.is_some() && !self.show_inline_completions_in_menu(cx) {
5261 self.update_visible_inline_completion(cx);
5262 }
5263 context_menu
5264 }
5265
5266 fn show_snippet_choices(
5267 &mut self,
5268 choices: &Vec<String>,
5269 selection: Range<Anchor>,
5270 cx: &mut ViewContext<Self>,
5271 ) {
5272 if selection.start.buffer_id.is_none() {
5273 return;
5274 }
5275 let buffer_id = selection.start.buffer_id.unwrap();
5276 let buffer = self.buffer().read(cx).buffer(buffer_id);
5277 let id = post_inc(&mut self.next_completion_id);
5278
5279 if let Some(buffer) = buffer {
5280 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
5281 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
5282 ));
5283 }
5284 }
5285
5286 pub fn insert_snippet(
5287 &mut self,
5288 insertion_ranges: &[Range<usize>],
5289 snippet: Snippet,
5290 cx: &mut ViewContext<Self>,
5291 ) -> Result<()> {
5292 struct Tabstop<T> {
5293 is_end_tabstop: bool,
5294 ranges: Vec<Range<T>>,
5295 choices: Option<Vec<String>>,
5296 }
5297
5298 let tabstops = self.buffer.update(cx, |buffer, cx| {
5299 let snippet_text: Arc<str> = snippet.text.clone().into();
5300 buffer.edit(
5301 insertion_ranges
5302 .iter()
5303 .cloned()
5304 .map(|range| (range, snippet_text.clone())),
5305 Some(AutoindentMode::EachLine),
5306 cx,
5307 );
5308
5309 let snapshot = &*buffer.read(cx);
5310 let snippet = &snippet;
5311 snippet
5312 .tabstops
5313 .iter()
5314 .map(|tabstop| {
5315 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
5316 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
5317 });
5318 let mut tabstop_ranges = tabstop
5319 .ranges
5320 .iter()
5321 .flat_map(|tabstop_range| {
5322 let mut delta = 0_isize;
5323 insertion_ranges.iter().map(move |insertion_range| {
5324 let insertion_start = insertion_range.start as isize + delta;
5325 delta +=
5326 snippet.text.len() as isize - insertion_range.len() as isize;
5327
5328 let start = ((insertion_start + tabstop_range.start) as usize)
5329 .min(snapshot.len());
5330 let end = ((insertion_start + tabstop_range.end) as usize)
5331 .min(snapshot.len());
5332 snapshot.anchor_before(start)..snapshot.anchor_after(end)
5333 })
5334 })
5335 .collect::<Vec<_>>();
5336 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
5337
5338 Tabstop {
5339 is_end_tabstop,
5340 ranges: tabstop_ranges,
5341 choices: tabstop.choices.clone(),
5342 }
5343 })
5344 .collect::<Vec<_>>()
5345 });
5346 if let Some(tabstop) = tabstops.first() {
5347 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5348 s.select_ranges(tabstop.ranges.iter().cloned());
5349 });
5350
5351 if let Some(choices) = &tabstop.choices {
5352 if let Some(selection) = tabstop.ranges.first() {
5353 self.show_snippet_choices(choices, selection.clone(), cx)
5354 }
5355 }
5356
5357 // If we're already at the last tabstop and it's at the end of the snippet,
5358 // we're done, we don't need to keep the state around.
5359 if !tabstop.is_end_tabstop {
5360 let choices = tabstops
5361 .iter()
5362 .map(|tabstop| tabstop.choices.clone())
5363 .collect();
5364
5365 let ranges = tabstops
5366 .into_iter()
5367 .map(|tabstop| tabstop.ranges)
5368 .collect::<Vec<_>>();
5369
5370 self.snippet_stack.push(SnippetState {
5371 active_index: 0,
5372 ranges,
5373 choices,
5374 });
5375 }
5376
5377 // Check whether the just-entered snippet ends with an auto-closable bracket.
5378 if self.autoclose_regions.is_empty() {
5379 let snapshot = self.buffer.read(cx).snapshot(cx);
5380 for selection in &mut self.selections.all::<Point>(cx) {
5381 let selection_head = selection.head();
5382 let Some(scope) = snapshot.language_scope_at(selection_head) else {
5383 continue;
5384 };
5385
5386 let mut bracket_pair = None;
5387 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
5388 let prev_chars = snapshot
5389 .reversed_chars_at(selection_head)
5390 .collect::<String>();
5391 for (pair, enabled) in scope.brackets() {
5392 if enabled
5393 && pair.close
5394 && prev_chars.starts_with(pair.start.as_str())
5395 && next_chars.starts_with(pair.end.as_str())
5396 {
5397 bracket_pair = Some(pair.clone());
5398 break;
5399 }
5400 }
5401 if let Some(pair) = bracket_pair {
5402 let start = snapshot.anchor_after(selection_head);
5403 let end = snapshot.anchor_after(selection_head);
5404 self.autoclose_regions.push(AutocloseRegion {
5405 selection_id: selection.id,
5406 range: start..end,
5407 pair,
5408 });
5409 }
5410 }
5411 }
5412 }
5413 Ok(())
5414 }
5415
5416 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5417 self.move_to_snippet_tabstop(Bias::Right, cx)
5418 }
5419
5420 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
5421 self.move_to_snippet_tabstop(Bias::Left, cx)
5422 }
5423
5424 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
5425 if let Some(mut snippet) = self.snippet_stack.pop() {
5426 match bias {
5427 Bias::Left => {
5428 if snippet.active_index > 0 {
5429 snippet.active_index -= 1;
5430 } else {
5431 self.snippet_stack.push(snippet);
5432 return false;
5433 }
5434 }
5435 Bias::Right => {
5436 if snippet.active_index + 1 < snippet.ranges.len() {
5437 snippet.active_index += 1;
5438 } else {
5439 self.snippet_stack.push(snippet);
5440 return false;
5441 }
5442 }
5443 }
5444 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
5445 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5446 s.select_anchor_ranges(current_ranges.iter().cloned())
5447 });
5448
5449 if let Some(choices) = &snippet.choices[snippet.active_index] {
5450 if let Some(selection) = current_ranges.first() {
5451 self.show_snippet_choices(&choices, selection.clone(), cx);
5452 }
5453 }
5454
5455 // If snippet state is not at the last tabstop, push it back on the stack
5456 if snippet.active_index + 1 < snippet.ranges.len() {
5457 self.snippet_stack.push(snippet);
5458 }
5459 return true;
5460 }
5461 }
5462
5463 false
5464 }
5465
5466 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
5467 self.transact(cx, |this, cx| {
5468 this.select_all(&SelectAll, cx);
5469 this.insert("", cx);
5470 });
5471 }
5472
5473 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
5474 self.transact(cx, |this, cx| {
5475 this.select_autoclose_pair(cx);
5476 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
5477 if !this.linked_edit_ranges.is_empty() {
5478 let selections = this.selections.all::<MultiBufferPoint>(cx);
5479 let snapshot = this.buffer.read(cx).snapshot(cx);
5480
5481 for selection in selections.iter() {
5482 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
5483 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
5484 if selection_start.buffer_id != selection_end.buffer_id {
5485 continue;
5486 }
5487 if let Some(ranges) =
5488 this.linked_editing_ranges_for(selection_start..selection_end, cx)
5489 {
5490 for (buffer, entries) in ranges {
5491 linked_ranges.entry(buffer).or_default().extend(entries);
5492 }
5493 }
5494 }
5495 }
5496
5497 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
5498 if !this.selections.line_mode {
5499 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
5500 for selection in &mut selections {
5501 if selection.is_empty() {
5502 let old_head = selection.head();
5503 let mut new_head =
5504 movement::left(&display_map, old_head.to_display_point(&display_map))
5505 .to_point(&display_map);
5506 if let Some((buffer, line_buffer_range)) = display_map
5507 .buffer_snapshot
5508 .buffer_line_for_row(MultiBufferRow(old_head.row))
5509 {
5510 let indent_size =
5511 buffer.indent_size_for_line(line_buffer_range.start.row);
5512 let indent_len = match indent_size.kind {
5513 IndentKind::Space => {
5514 buffer.settings_at(line_buffer_range.start, cx).tab_size
5515 }
5516 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
5517 };
5518 if old_head.column <= indent_size.len && old_head.column > 0 {
5519 let indent_len = indent_len.get();
5520 new_head = cmp::min(
5521 new_head,
5522 MultiBufferPoint::new(
5523 old_head.row,
5524 ((old_head.column - 1) / indent_len) * indent_len,
5525 ),
5526 );
5527 }
5528 }
5529
5530 selection.set_head(new_head, SelectionGoal::None);
5531 }
5532 }
5533 }
5534
5535 this.signature_help_state.set_backspace_pressed(true);
5536 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5537 this.insert("", cx);
5538 let empty_str: Arc<str> = Arc::from("");
5539 for (buffer, edits) in linked_ranges {
5540 let snapshot = buffer.read(cx).snapshot();
5541 use text::ToPoint as TP;
5542
5543 let edits = edits
5544 .into_iter()
5545 .map(|range| {
5546 let end_point = TP::to_point(&range.end, &snapshot);
5547 let mut start_point = TP::to_point(&range.start, &snapshot);
5548
5549 if end_point == start_point {
5550 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
5551 .saturating_sub(1);
5552 start_point =
5553 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
5554 };
5555
5556 (start_point..end_point, empty_str.clone())
5557 })
5558 .sorted_by_key(|(range, _)| range.start)
5559 .collect::<Vec<_>>();
5560 buffer.update(cx, |this, cx| {
5561 this.edit(edits, None, cx);
5562 })
5563 }
5564 this.refresh_inline_completion(true, false, cx);
5565 linked_editing_ranges::refresh_linked_ranges(this, cx);
5566 });
5567 }
5568
5569 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
5570 self.transact(cx, |this, cx| {
5571 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5572 let line_mode = s.line_mode;
5573 s.move_with(|map, selection| {
5574 if selection.is_empty() && !line_mode {
5575 let cursor = movement::right(map, selection.head());
5576 selection.end = cursor;
5577 selection.reversed = true;
5578 selection.goal = SelectionGoal::None;
5579 }
5580 })
5581 });
5582 this.insert("", cx);
5583 this.refresh_inline_completion(true, false, cx);
5584 });
5585 }
5586
5587 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
5588 if self.move_to_prev_snippet_tabstop(cx) {
5589 return;
5590 }
5591
5592 self.outdent(&Outdent, cx);
5593 }
5594
5595 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
5596 if self.move_to_next_snippet_tabstop(cx) || self.read_only(cx) {
5597 return;
5598 }
5599
5600 let mut selections = self.selections.all_adjusted(cx);
5601 let buffer = self.buffer.read(cx);
5602 let snapshot = buffer.snapshot(cx);
5603 let rows_iter = selections.iter().map(|s| s.head().row);
5604 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
5605
5606 let mut edits = Vec::new();
5607 let mut prev_edited_row = 0;
5608 let mut row_delta = 0;
5609 for selection in &mut selections {
5610 if selection.start.row != prev_edited_row {
5611 row_delta = 0;
5612 }
5613 prev_edited_row = selection.end.row;
5614
5615 // If the selection is non-empty, then increase the indentation of the selected lines.
5616 if !selection.is_empty() {
5617 row_delta =
5618 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5619 continue;
5620 }
5621
5622 // If the selection is empty and the cursor is in the leading whitespace before the
5623 // suggested indentation, then auto-indent the line.
5624 let cursor = selection.head();
5625 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
5626 if let Some(suggested_indent) =
5627 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
5628 {
5629 if cursor.column < suggested_indent.len
5630 && cursor.column <= current_indent.len
5631 && current_indent.len <= suggested_indent.len
5632 {
5633 selection.start = Point::new(cursor.row, suggested_indent.len);
5634 selection.end = selection.start;
5635 if row_delta == 0 {
5636 edits.extend(Buffer::edit_for_indent_size_adjustment(
5637 cursor.row,
5638 current_indent,
5639 suggested_indent,
5640 ));
5641 row_delta = suggested_indent.len - current_indent.len;
5642 }
5643 continue;
5644 }
5645 }
5646
5647 // Otherwise, insert a hard or soft tab.
5648 let settings = buffer.settings_at(cursor, cx);
5649 let tab_size = if settings.hard_tabs {
5650 IndentSize::tab()
5651 } else {
5652 let tab_size = settings.tab_size.get();
5653 let char_column = snapshot
5654 .text_for_range(Point::new(cursor.row, 0)..cursor)
5655 .flat_map(str::chars)
5656 .count()
5657 + row_delta as usize;
5658 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
5659 IndentSize::spaces(chars_to_next_tab_stop)
5660 };
5661 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
5662 selection.end = selection.start;
5663 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
5664 row_delta += tab_size.len;
5665 }
5666
5667 self.transact(cx, |this, cx| {
5668 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5669 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5670 this.refresh_inline_completion(true, false, cx);
5671 });
5672 }
5673
5674 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
5675 if self.read_only(cx) {
5676 return;
5677 }
5678 let mut selections = self.selections.all::<Point>(cx);
5679 let mut prev_edited_row = 0;
5680 let mut row_delta = 0;
5681 let mut edits = Vec::new();
5682 let buffer = self.buffer.read(cx);
5683 let snapshot = buffer.snapshot(cx);
5684 for selection in &mut selections {
5685 if selection.start.row != prev_edited_row {
5686 row_delta = 0;
5687 }
5688 prev_edited_row = selection.end.row;
5689
5690 row_delta =
5691 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
5692 }
5693
5694 self.transact(cx, |this, cx| {
5695 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
5696 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5697 });
5698 }
5699
5700 fn indent_selection(
5701 buffer: &MultiBuffer,
5702 snapshot: &MultiBufferSnapshot,
5703 selection: &mut Selection<Point>,
5704 edits: &mut Vec<(Range<Point>, String)>,
5705 delta_for_start_row: u32,
5706 cx: &AppContext,
5707 ) -> u32 {
5708 let settings = buffer.settings_at(selection.start, cx);
5709 let tab_size = settings.tab_size.get();
5710 let indent_kind = if settings.hard_tabs {
5711 IndentKind::Tab
5712 } else {
5713 IndentKind::Space
5714 };
5715 let mut start_row = selection.start.row;
5716 let mut end_row = selection.end.row + 1;
5717
5718 // If a selection ends at the beginning of a line, don't indent
5719 // that last line.
5720 if selection.end.column == 0 && selection.end.row > selection.start.row {
5721 end_row -= 1;
5722 }
5723
5724 // Avoid re-indenting a row that has already been indented by a
5725 // previous selection, but still update this selection's column
5726 // to reflect that indentation.
5727 if delta_for_start_row > 0 {
5728 start_row += 1;
5729 selection.start.column += delta_for_start_row;
5730 if selection.end.row == selection.start.row {
5731 selection.end.column += delta_for_start_row;
5732 }
5733 }
5734
5735 let mut delta_for_end_row = 0;
5736 let has_multiple_rows = start_row + 1 != end_row;
5737 for row in start_row..end_row {
5738 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
5739 let indent_delta = match (current_indent.kind, indent_kind) {
5740 (IndentKind::Space, IndentKind::Space) => {
5741 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
5742 IndentSize::spaces(columns_to_next_tab_stop)
5743 }
5744 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
5745 (_, IndentKind::Tab) => IndentSize::tab(),
5746 };
5747
5748 let start = if has_multiple_rows || current_indent.len < selection.start.column {
5749 0
5750 } else {
5751 selection.start.column
5752 };
5753 let row_start = Point::new(row, start);
5754 edits.push((
5755 row_start..row_start,
5756 indent_delta.chars().collect::<String>(),
5757 ));
5758
5759 // Update this selection's endpoints to reflect the indentation.
5760 if row == selection.start.row {
5761 selection.start.column += indent_delta.len;
5762 }
5763 if row == selection.end.row {
5764 selection.end.column += indent_delta.len;
5765 delta_for_end_row = indent_delta.len;
5766 }
5767 }
5768
5769 if selection.start.row == selection.end.row {
5770 delta_for_start_row + delta_for_end_row
5771 } else {
5772 delta_for_end_row
5773 }
5774 }
5775
5776 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
5777 if self.read_only(cx) {
5778 return;
5779 }
5780 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5781 let selections = self.selections.all::<Point>(cx);
5782 let mut deletion_ranges = Vec::new();
5783 let mut last_outdent = None;
5784 {
5785 let buffer = self.buffer.read(cx);
5786 let snapshot = buffer.snapshot(cx);
5787 for selection in &selections {
5788 let settings = buffer.settings_at(selection.start, cx);
5789 let tab_size = settings.tab_size.get();
5790 let mut rows = selection.spanned_rows(false, &display_map);
5791
5792 // Avoid re-outdenting a row that has already been outdented by a
5793 // previous selection.
5794 if let Some(last_row) = last_outdent {
5795 if last_row == rows.start {
5796 rows.start = rows.start.next_row();
5797 }
5798 }
5799 let has_multiple_rows = rows.len() > 1;
5800 for row in rows.iter_rows() {
5801 let indent_size = snapshot.indent_size_for_line(row);
5802 if indent_size.len > 0 {
5803 let deletion_len = match indent_size.kind {
5804 IndentKind::Space => {
5805 let columns_to_prev_tab_stop = indent_size.len % tab_size;
5806 if columns_to_prev_tab_stop == 0 {
5807 tab_size
5808 } else {
5809 columns_to_prev_tab_stop
5810 }
5811 }
5812 IndentKind::Tab => 1,
5813 };
5814 let start = if has_multiple_rows
5815 || deletion_len > selection.start.column
5816 || indent_size.len < selection.start.column
5817 {
5818 0
5819 } else {
5820 selection.start.column - deletion_len
5821 };
5822 deletion_ranges.push(
5823 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
5824 );
5825 last_outdent = Some(row);
5826 }
5827 }
5828 }
5829 }
5830
5831 self.transact(cx, |this, cx| {
5832 this.buffer.update(cx, |buffer, cx| {
5833 let empty_str: Arc<str> = Arc::default();
5834 buffer.edit(
5835 deletion_ranges
5836 .into_iter()
5837 .map(|range| (range, empty_str.clone())),
5838 None,
5839 cx,
5840 );
5841 });
5842 let selections = this.selections.all::<usize>(cx);
5843 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5844 });
5845 }
5846
5847 pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
5848 if self.read_only(cx) {
5849 return;
5850 }
5851 let selections = self
5852 .selections
5853 .all::<usize>(cx)
5854 .into_iter()
5855 .map(|s| s.range());
5856
5857 self.transact(cx, |this, cx| {
5858 this.buffer.update(cx, |buffer, cx| {
5859 buffer.autoindent_ranges(selections, cx);
5860 });
5861 let selections = this.selections.all::<usize>(cx);
5862 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5863 });
5864 }
5865
5866 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
5867 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5868 let selections = self.selections.all::<Point>(cx);
5869
5870 let mut new_cursors = Vec::new();
5871 let mut edit_ranges = Vec::new();
5872 let mut selections = selections.iter().peekable();
5873 while let Some(selection) = selections.next() {
5874 let mut rows = selection.spanned_rows(false, &display_map);
5875 let goal_display_column = selection.head().to_display_point(&display_map).column();
5876
5877 // Accumulate contiguous regions of rows that we want to delete.
5878 while let Some(next_selection) = selections.peek() {
5879 let next_rows = next_selection.spanned_rows(false, &display_map);
5880 if next_rows.start <= rows.end {
5881 rows.end = next_rows.end;
5882 selections.next().unwrap();
5883 } else {
5884 break;
5885 }
5886 }
5887
5888 let buffer = &display_map.buffer_snapshot;
5889 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
5890 let edit_end;
5891 let cursor_buffer_row;
5892 if buffer.max_point().row >= rows.end.0 {
5893 // If there's a line after the range, delete the \n from the end of the row range
5894 // and position the cursor on the next line.
5895 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
5896 cursor_buffer_row = rows.end;
5897 } else {
5898 // If there isn't a line after the range, delete the \n from the line before the
5899 // start of the row range and position the cursor there.
5900 edit_start = edit_start.saturating_sub(1);
5901 edit_end = buffer.len();
5902 cursor_buffer_row = rows.start.previous_row();
5903 }
5904
5905 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
5906 *cursor.column_mut() =
5907 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
5908
5909 new_cursors.push((
5910 selection.id,
5911 buffer.anchor_after(cursor.to_point(&display_map)),
5912 ));
5913 edit_ranges.push(edit_start..edit_end);
5914 }
5915
5916 self.transact(cx, |this, cx| {
5917 let buffer = this.buffer.update(cx, |buffer, cx| {
5918 let empty_str: Arc<str> = Arc::default();
5919 buffer.edit(
5920 edit_ranges
5921 .into_iter()
5922 .map(|range| (range, empty_str.clone())),
5923 None,
5924 cx,
5925 );
5926 buffer.snapshot(cx)
5927 });
5928 let new_selections = new_cursors
5929 .into_iter()
5930 .map(|(id, cursor)| {
5931 let cursor = cursor.to_point(&buffer);
5932 Selection {
5933 id,
5934 start: cursor,
5935 end: cursor,
5936 reversed: false,
5937 goal: SelectionGoal::None,
5938 }
5939 })
5940 .collect();
5941
5942 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5943 s.select(new_selections);
5944 });
5945 });
5946 }
5947
5948 pub fn join_lines_impl(&mut self, insert_whitespace: bool, cx: &mut ViewContext<Self>) {
5949 if self.read_only(cx) {
5950 return;
5951 }
5952 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
5953 for selection in self.selections.all::<Point>(cx) {
5954 let start = MultiBufferRow(selection.start.row);
5955 // Treat single line selections as if they include the next line. Otherwise this action
5956 // would do nothing for single line selections individual cursors.
5957 let end = if selection.start.row == selection.end.row {
5958 MultiBufferRow(selection.start.row + 1)
5959 } else {
5960 MultiBufferRow(selection.end.row)
5961 };
5962
5963 if let Some(last_row_range) = row_ranges.last_mut() {
5964 if start <= last_row_range.end {
5965 last_row_range.end = end;
5966 continue;
5967 }
5968 }
5969 row_ranges.push(start..end);
5970 }
5971
5972 let snapshot = self.buffer.read(cx).snapshot(cx);
5973 let mut cursor_positions = Vec::new();
5974 for row_range in &row_ranges {
5975 let anchor = snapshot.anchor_before(Point::new(
5976 row_range.end.previous_row().0,
5977 snapshot.line_len(row_range.end.previous_row()),
5978 ));
5979 cursor_positions.push(anchor..anchor);
5980 }
5981
5982 self.transact(cx, |this, cx| {
5983 for row_range in row_ranges.into_iter().rev() {
5984 for row in row_range.iter_rows().rev() {
5985 let end_of_line = Point::new(row.0, snapshot.line_len(row));
5986 let next_line_row = row.next_row();
5987 let indent = snapshot.indent_size_for_line(next_line_row);
5988 let start_of_next_line = Point::new(next_line_row.0, indent.len);
5989
5990 let replace =
5991 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
5992 " "
5993 } else {
5994 ""
5995 };
5996
5997 this.buffer.update(cx, |buffer, cx| {
5998 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
5999 });
6000 }
6001 }
6002
6003 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6004 s.select_anchor_ranges(cursor_positions)
6005 });
6006 });
6007 }
6008
6009 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
6010 self.join_lines_impl(true, cx);
6011 }
6012
6013 pub fn sort_lines_case_sensitive(
6014 &mut self,
6015 _: &SortLinesCaseSensitive,
6016 cx: &mut ViewContext<Self>,
6017 ) {
6018 self.manipulate_lines(cx, |lines| lines.sort())
6019 }
6020
6021 pub fn sort_lines_case_insensitive(
6022 &mut self,
6023 _: &SortLinesCaseInsensitive,
6024 cx: &mut ViewContext<Self>,
6025 ) {
6026 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
6027 }
6028
6029 pub fn unique_lines_case_insensitive(
6030 &mut self,
6031 _: &UniqueLinesCaseInsensitive,
6032 cx: &mut ViewContext<Self>,
6033 ) {
6034 self.manipulate_lines(cx, |lines| {
6035 let mut seen = HashSet::default();
6036 lines.retain(|line| seen.insert(line.to_lowercase()));
6037 })
6038 }
6039
6040 pub fn unique_lines_case_sensitive(
6041 &mut self,
6042 _: &UniqueLinesCaseSensitive,
6043 cx: &mut ViewContext<Self>,
6044 ) {
6045 self.manipulate_lines(cx, |lines| {
6046 let mut seen = HashSet::default();
6047 lines.retain(|line| seen.insert(*line));
6048 })
6049 }
6050
6051 pub fn revert_file(&mut self, _: &RevertFile, cx: &mut ViewContext<Self>) {
6052 let mut revert_changes = HashMap::default();
6053 let snapshot = self.snapshot(cx);
6054 for hunk in hunks_for_ranges(
6055 Some(Point::zero()..snapshot.buffer_snapshot.max_point()).into_iter(),
6056 &snapshot,
6057 ) {
6058 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6059 }
6060 if !revert_changes.is_empty() {
6061 self.transact(cx, |editor, cx| {
6062 editor.revert(revert_changes, cx);
6063 });
6064 }
6065 }
6066
6067 pub fn reload_file(&mut self, _: &ReloadFile, cx: &mut ViewContext<Self>) {
6068 let Some(project) = self.project.clone() else {
6069 return;
6070 };
6071 self.reload(project, cx).detach_and_notify_err(cx);
6072 }
6073
6074 pub fn revert_selected_hunks(&mut self, _: &RevertSelectedHunks, cx: &mut ViewContext<Self>) {
6075 let revert_changes = self.gather_revert_changes(&self.selections.all(cx), cx);
6076 if !revert_changes.is_empty() {
6077 self.transact(cx, |editor, cx| {
6078 editor.revert(revert_changes, cx);
6079 });
6080 }
6081 }
6082
6083 fn revert_hunk(&mut self, hunk: HoveredHunk, cx: &mut ViewContext<Editor>) {
6084 let snapshot = self.buffer.read(cx).read(cx);
6085 if let Some(hunk) = crate::hunk_diff::to_diff_hunk(&hunk, &snapshot) {
6086 drop(snapshot);
6087 let mut revert_changes = HashMap::default();
6088 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6089 if !revert_changes.is_empty() {
6090 self.revert(revert_changes, cx)
6091 }
6092 }
6093 }
6094
6095 pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
6096 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
6097 let project_path = buffer.read(cx).project_path(cx)?;
6098 let project = self.project.as_ref()?.read(cx);
6099 let entry = project.entry_for_path(&project_path, cx)?;
6100 let parent = match &entry.canonical_path {
6101 Some(canonical_path) => canonical_path.to_path_buf(),
6102 None => project.absolute_path(&project_path, cx)?,
6103 }
6104 .parent()?
6105 .to_path_buf();
6106 Some(parent)
6107 }) {
6108 cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
6109 }
6110 }
6111
6112 fn gather_revert_changes(
6113 &mut self,
6114 selections: &[Selection<Point>],
6115 cx: &mut ViewContext<Editor>,
6116 ) -> HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>> {
6117 let mut revert_changes = HashMap::default();
6118 let snapshot = self.snapshot(cx);
6119 for hunk in hunks_for_selections(&snapshot, selections) {
6120 self.prepare_revert_change(&mut revert_changes, &hunk, cx);
6121 }
6122 revert_changes
6123 }
6124
6125 pub fn prepare_revert_change(
6126 &mut self,
6127 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
6128 hunk: &MultiBufferDiffHunk,
6129 cx: &AppContext,
6130 ) -> Option<()> {
6131 let buffer = self.buffer.read(cx).buffer(hunk.buffer_id)?;
6132 let buffer = buffer.read(cx);
6133 let change_set = &self.diff_map.diff_bases.get(&hunk.buffer_id)?.change_set;
6134 let original_text = change_set
6135 .read(cx)
6136 .base_text
6137 .as_ref()?
6138 .read(cx)
6139 .as_rope()
6140 .slice(hunk.diff_base_byte_range.clone());
6141 let buffer_snapshot = buffer.snapshot();
6142 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
6143 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
6144 probe
6145 .0
6146 .start
6147 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
6148 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
6149 }) {
6150 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
6151 Some(())
6152 } else {
6153 None
6154 }
6155 }
6156
6157 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
6158 self.manipulate_lines(cx, |lines| lines.reverse())
6159 }
6160
6161 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
6162 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
6163 }
6164
6165 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6166 where
6167 Fn: FnMut(&mut Vec<&str>),
6168 {
6169 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6170 let buffer = self.buffer.read(cx).snapshot(cx);
6171
6172 let mut edits = Vec::new();
6173
6174 let selections = self.selections.all::<Point>(cx);
6175 let mut selections = selections.iter().peekable();
6176 let mut contiguous_row_selections = Vec::new();
6177 let mut new_selections = Vec::new();
6178 let mut added_lines = 0;
6179 let mut removed_lines = 0;
6180
6181 while let Some(selection) = selections.next() {
6182 let (start_row, end_row) = consume_contiguous_rows(
6183 &mut contiguous_row_selections,
6184 selection,
6185 &display_map,
6186 &mut selections,
6187 );
6188
6189 let start_point = Point::new(start_row.0, 0);
6190 let end_point = Point::new(
6191 end_row.previous_row().0,
6192 buffer.line_len(end_row.previous_row()),
6193 );
6194 let text = buffer
6195 .text_for_range(start_point..end_point)
6196 .collect::<String>();
6197
6198 let mut lines = text.split('\n').collect_vec();
6199
6200 let lines_before = lines.len();
6201 callback(&mut lines);
6202 let lines_after = lines.len();
6203
6204 edits.push((start_point..end_point, lines.join("\n")));
6205
6206 // Selections must change based on added and removed line count
6207 let start_row =
6208 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
6209 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
6210 new_selections.push(Selection {
6211 id: selection.id,
6212 start: start_row,
6213 end: end_row,
6214 goal: SelectionGoal::None,
6215 reversed: selection.reversed,
6216 });
6217
6218 if lines_after > lines_before {
6219 added_lines += lines_after - lines_before;
6220 } else if lines_before > lines_after {
6221 removed_lines += lines_before - lines_after;
6222 }
6223 }
6224
6225 self.transact(cx, |this, cx| {
6226 let buffer = this.buffer.update(cx, |buffer, cx| {
6227 buffer.edit(edits, None, cx);
6228 buffer.snapshot(cx)
6229 });
6230
6231 // Recalculate offsets on newly edited buffer
6232 let new_selections = new_selections
6233 .iter()
6234 .map(|s| {
6235 let start_point = Point::new(s.start.0, 0);
6236 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
6237 Selection {
6238 id: s.id,
6239 start: buffer.point_to_offset(start_point),
6240 end: buffer.point_to_offset(end_point),
6241 goal: s.goal,
6242 reversed: s.reversed,
6243 }
6244 })
6245 .collect();
6246
6247 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6248 s.select(new_selections);
6249 });
6250
6251 this.request_autoscroll(Autoscroll::fit(), cx);
6252 });
6253 }
6254
6255 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
6256 self.manipulate_text(cx, |text| text.to_uppercase())
6257 }
6258
6259 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
6260 self.manipulate_text(cx, |text| text.to_lowercase())
6261 }
6262
6263 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
6264 self.manipulate_text(cx, |text| {
6265 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6266 // https://github.com/rutrum/convert-case/issues/16
6267 text.split('\n')
6268 .map(|line| line.to_case(Case::Title))
6269 .join("\n")
6270 })
6271 }
6272
6273 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
6274 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
6275 }
6276
6277 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
6278 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
6279 }
6280
6281 pub fn convert_to_upper_camel_case(
6282 &mut self,
6283 _: &ConvertToUpperCamelCase,
6284 cx: &mut ViewContext<Self>,
6285 ) {
6286 self.manipulate_text(cx, |text| {
6287 // Hack to get around the fact that to_case crate doesn't support '\n' as a word boundary
6288 // https://github.com/rutrum/convert-case/issues/16
6289 text.split('\n')
6290 .map(|line| line.to_case(Case::UpperCamel))
6291 .join("\n")
6292 })
6293 }
6294
6295 pub fn convert_to_lower_camel_case(
6296 &mut self,
6297 _: &ConvertToLowerCamelCase,
6298 cx: &mut ViewContext<Self>,
6299 ) {
6300 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
6301 }
6302
6303 pub fn convert_to_opposite_case(
6304 &mut self,
6305 _: &ConvertToOppositeCase,
6306 cx: &mut ViewContext<Self>,
6307 ) {
6308 self.manipulate_text(cx, |text| {
6309 text.chars()
6310 .fold(String::with_capacity(text.len()), |mut t, c| {
6311 if c.is_uppercase() {
6312 t.extend(c.to_lowercase());
6313 } else {
6314 t.extend(c.to_uppercase());
6315 }
6316 t
6317 })
6318 })
6319 }
6320
6321 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
6322 where
6323 Fn: FnMut(&str) -> String,
6324 {
6325 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6326 let buffer = self.buffer.read(cx).snapshot(cx);
6327
6328 let mut new_selections = Vec::new();
6329 let mut edits = Vec::new();
6330 let mut selection_adjustment = 0i32;
6331
6332 for selection in self.selections.all::<usize>(cx) {
6333 let selection_is_empty = selection.is_empty();
6334
6335 let (start, end) = if selection_is_empty {
6336 let word_range = movement::surrounding_word(
6337 &display_map,
6338 selection.start.to_display_point(&display_map),
6339 );
6340 let start = word_range.start.to_offset(&display_map, Bias::Left);
6341 let end = word_range.end.to_offset(&display_map, Bias::Left);
6342 (start, end)
6343 } else {
6344 (selection.start, selection.end)
6345 };
6346
6347 let text = buffer.text_for_range(start..end).collect::<String>();
6348 let old_length = text.len() as i32;
6349 let text = callback(&text);
6350
6351 new_selections.push(Selection {
6352 start: (start as i32 - selection_adjustment) as usize,
6353 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
6354 goal: SelectionGoal::None,
6355 ..selection
6356 });
6357
6358 selection_adjustment += old_length - text.len() as i32;
6359
6360 edits.push((start..end, text));
6361 }
6362
6363 self.transact(cx, |this, cx| {
6364 this.buffer.update(cx, |buffer, cx| {
6365 buffer.edit(edits, None, cx);
6366 });
6367
6368 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6369 s.select(new_selections);
6370 });
6371
6372 this.request_autoscroll(Autoscroll::fit(), cx);
6373 });
6374 }
6375
6376 pub fn duplicate(&mut self, upwards: bool, whole_lines: bool, cx: &mut ViewContext<Self>) {
6377 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6378 let buffer = &display_map.buffer_snapshot;
6379 let selections = self.selections.all::<Point>(cx);
6380
6381 let mut edits = Vec::new();
6382 let mut selections_iter = selections.iter().peekable();
6383 while let Some(selection) = selections_iter.next() {
6384 let mut rows = selection.spanned_rows(false, &display_map);
6385 // duplicate line-wise
6386 if whole_lines || selection.start == selection.end {
6387 // Avoid duplicating the same lines twice.
6388 while let Some(next_selection) = selections_iter.peek() {
6389 let next_rows = next_selection.spanned_rows(false, &display_map);
6390 if next_rows.start < rows.end {
6391 rows.end = next_rows.end;
6392 selections_iter.next().unwrap();
6393 } else {
6394 break;
6395 }
6396 }
6397
6398 // Copy the text from the selected row region and splice it either at the start
6399 // or end of the region.
6400 let start = Point::new(rows.start.0, 0);
6401 let end = Point::new(
6402 rows.end.previous_row().0,
6403 buffer.line_len(rows.end.previous_row()),
6404 );
6405 let text = buffer
6406 .text_for_range(start..end)
6407 .chain(Some("\n"))
6408 .collect::<String>();
6409 let insert_location = if upwards {
6410 Point::new(rows.end.0, 0)
6411 } else {
6412 start
6413 };
6414 edits.push((insert_location..insert_location, text));
6415 } else {
6416 // duplicate character-wise
6417 let start = selection.start;
6418 let end = selection.end;
6419 let text = buffer.text_for_range(start..end).collect::<String>();
6420 edits.push((selection.end..selection.end, text));
6421 }
6422 }
6423
6424 self.transact(cx, |this, cx| {
6425 this.buffer.update(cx, |buffer, cx| {
6426 buffer.edit(edits, None, cx);
6427 });
6428
6429 this.request_autoscroll(Autoscroll::fit(), cx);
6430 });
6431 }
6432
6433 pub fn duplicate_line_up(&mut self, _: &DuplicateLineUp, cx: &mut ViewContext<Self>) {
6434 self.duplicate(true, true, cx);
6435 }
6436
6437 pub fn duplicate_line_down(&mut self, _: &DuplicateLineDown, cx: &mut ViewContext<Self>) {
6438 self.duplicate(false, true, cx);
6439 }
6440
6441 pub fn duplicate_selection(&mut self, _: &DuplicateSelection, cx: &mut ViewContext<Self>) {
6442 self.duplicate(false, false, cx);
6443 }
6444
6445 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
6446 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6447 let buffer = self.buffer.read(cx).snapshot(cx);
6448
6449 let mut edits = Vec::new();
6450 let mut unfold_ranges = Vec::new();
6451 let mut refold_creases = Vec::new();
6452
6453 let selections = self.selections.all::<Point>(cx);
6454 let mut selections = selections.iter().peekable();
6455 let mut contiguous_row_selections = Vec::new();
6456 let mut new_selections = Vec::new();
6457
6458 while let Some(selection) = selections.next() {
6459 // Find all the selections that span a contiguous row range
6460 let (start_row, end_row) = consume_contiguous_rows(
6461 &mut contiguous_row_selections,
6462 selection,
6463 &display_map,
6464 &mut selections,
6465 );
6466
6467 // Move the text spanned by the row range to be before the line preceding the row range
6468 if start_row.0 > 0 {
6469 let range_to_move = Point::new(
6470 start_row.previous_row().0,
6471 buffer.line_len(start_row.previous_row()),
6472 )
6473 ..Point::new(
6474 end_row.previous_row().0,
6475 buffer.line_len(end_row.previous_row()),
6476 );
6477 let insertion_point = display_map
6478 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
6479 .0;
6480
6481 // Don't move lines across excerpts
6482 if buffer
6483 .excerpt_boundaries_in_range((
6484 Bound::Excluded(insertion_point),
6485 Bound::Included(range_to_move.end),
6486 ))
6487 .next()
6488 .is_none()
6489 {
6490 let text = buffer
6491 .text_for_range(range_to_move.clone())
6492 .flat_map(|s| s.chars())
6493 .skip(1)
6494 .chain(['\n'])
6495 .collect::<String>();
6496
6497 edits.push((
6498 buffer.anchor_after(range_to_move.start)
6499 ..buffer.anchor_before(range_to_move.end),
6500 String::new(),
6501 ));
6502 let insertion_anchor = buffer.anchor_after(insertion_point);
6503 edits.push((insertion_anchor..insertion_anchor, text));
6504
6505 let row_delta = range_to_move.start.row - insertion_point.row + 1;
6506
6507 // Move selections up
6508 new_selections.extend(contiguous_row_selections.drain(..).map(
6509 |mut selection| {
6510 selection.start.row -= row_delta;
6511 selection.end.row -= row_delta;
6512 selection
6513 },
6514 ));
6515
6516 // Move folds up
6517 unfold_ranges.push(range_to_move.clone());
6518 for fold in display_map.folds_in_range(
6519 buffer.anchor_before(range_to_move.start)
6520 ..buffer.anchor_after(range_to_move.end),
6521 ) {
6522 let mut start = fold.range.start.to_point(&buffer);
6523 let mut end = fold.range.end.to_point(&buffer);
6524 start.row -= row_delta;
6525 end.row -= row_delta;
6526 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6527 }
6528 }
6529 }
6530
6531 // If we didn't move line(s), preserve the existing selections
6532 new_selections.append(&mut contiguous_row_selections);
6533 }
6534
6535 self.transact(cx, |this, cx| {
6536 this.unfold_ranges(&unfold_ranges, true, true, cx);
6537 this.buffer.update(cx, |buffer, cx| {
6538 for (range, text) in edits {
6539 buffer.edit([(range, text)], None, cx);
6540 }
6541 });
6542 this.fold_creases(refold_creases, true, cx);
6543 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6544 s.select(new_selections);
6545 })
6546 });
6547 }
6548
6549 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
6550 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6551 let buffer = self.buffer.read(cx).snapshot(cx);
6552
6553 let mut edits = Vec::new();
6554 let mut unfold_ranges = Vec::new();
6555 let mut refold_creases = Vec::new();
6556
6557 let selections = self.selections.all::<Point>(cx);
6558 let mut selections = selections.iter().peekable();
6559 let mut contiguous_row_selections = Vec::new();
6560 let mut new_selections = Vec::new();
6561
6562 while let Some(selection) = selections.next() {
6563 // Find all the selections that span a contiguous row range
6564 let (start_row, end_row) = consume_contiguous_rows(
6565 &mut contiguous_row_selections,
6566 selection,
6567 &display_map,
6568 &mut selections,
6569 );
6570
6571 // Move the text spanned by the row range to be after the last line of the row range
6572 if end_row.0 <= buffer.max_point().row {
6573 let range_to_move =
6574 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
6575 let insertion_point = display_map
6576 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
6577 .0;
6578
6579 // Don't move lines across excerpt boundaries
6580 if buffer
6581 .excerpt_boundaries_in_range((
6582 Bound::Excluded(range_to_move.start),
6583 Bound::Included(insertion_point),
6584 ))
6585 .next()
6586 .is_none()
6587 {
6588 let mut text = String::from("\n");
6589 text.extend(buffer.text_for_range(range_to_move.clone()));
6590 text.pop(); // Drop trailing newline
6591 edits.push((
6592 buffer.anchor_after(range_to_move.start)
6593 ..buffer.anchor_before(range_to_move.end),
6594 String::new(),
6595 ));
6596 let insertion_anchor = buffer.anchor_after(insertion_point);
6597 edits.push((insertion_anchor..insertion_anchor, text));
6598
6599 let row_delta = insertion_point.row - range_to_move.end.row + 1;
6600
6601 // Move selections down
6602 new_selections.extend(contiguous_row_selections.drain(..).map(
6603 |mut selection| {
6604 selection.start.row += row_delta;
6605 selection.end.row += row_delta;
6606 selection
6607 },
6608 ));
6609
6610 // Move folds down
6611 unfold_ranges.push(range_to_move.clone());
6612 for fold in display_map.folds_in_range(
6613 buffer.anchor_before(range_to_move.start)
6614 ..buffer.anchor_after(range_to_move.end),
6615 ) {
6616 let mut start = fold.range.start.to_point(&buffer);
6617 let mut end = fold.range.end.to_point(&buffer);
6618 start.row += row_delta;
6619 end.row += row_delta;
6620 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
6621 }
6622 }
6623 }
6624
6625 // If we didn't move line(s), preserve the existing selections
6626 new_selections.append(&mut contiguous_row_selections);
6627 }
6628
6629 self.transact(cx, |this, cx| {
6630 this.unfold_ranges(&unfold_ranges, true, true, cx);
6631 this.buffer.update(cx, |buffer, cx| {
6632 for (range, text) in edits {
6633 buffer.edit([(range, text)], None, cx);
6634 }
6635 });
6636 this.fold_creases(refold_creases, true, cx);
6637 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
6638 });
6639 }
6640
6641 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
6642 let text_layout_details = &self.text_layout_details(cx);
6643 self.transact(cx, |this, cx| {
6644 let edits = this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6645 let mut edits: Vec<(Range<usize>, String)> = Default::default();
6646 let line_mode = s.line_mode;
6647 s.move_with(|display_map, selection| {
6648 if !selection.is_empty() || line_mode {
6649 return;
6650 }
6651
6652 let mut head = selection.head();
6653 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
6654 if head.column() == display_map.line_len(head.row()) {
6655 transpose_offset = display_map
6656 .buffer_snapshot
6657 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6658 }
6659
6660 if transpose_offset == 0 {
6661 return;
6662 }
6663
6664 *head.column_mut() += 1;
6665 head = display_map.clip_point(head, Bias::Right);
6666 let goal = SelectionGoal::HorizontalPosition(
6667 display_map
6668 .x_for_display_point(head, text_layout_details)
6669 .into(),
6670 );
6671 selection.collapse_to(head, goal);
6672
6673 let transpose_start = display_map
6674 .buffer_snapshot
6675 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
6676 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
6677 let transpose_end = display_map
6678 .buffer_snapshot
6679 .clip_offset(transpose_offset + 1, Bias::Right);
6680 if let Some(ch) =
6681 display_map.buffer_snapshot.chars_at(transpose_start).next()
6682 {
6683 edits.push((transpose_start..transpose_offset, String::new()));
6684 edits.push((transpose_end..transpose_end, ch.to_string()));
6685 }
6686 }
6687 });
6688 edits
6689 });
6690 this.buffer
6691 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6692 let selections = this.selections.all::<usize>(cx);
6693 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6694 s.select(selections);
6695 });
6696 });
6697 }
6698
6699 pub fn rewrap(&mut self, _: &Rewrap, cx: &mut ViewContext<Self>) {
6700 self.rewrap_impl(IsVimMode::No, cx)
6701 }
6702
6703 pub fn rewrap_impl(&mut self, is_vim_mode: IsVimMode, cx: &mut ViewContext<Self>) {
6704 let buffer = self.buffer.read(cx).snapshot(cx);
6705 let selections = self.selections.all::<Point>(cx);
6706 let mut selections = selections.iter().peekable();
6707
6708 let mut edits = Vec::new();
6709 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
6710
6711 while let Some(selection) = selections.next() {
6712 let mut start_row = selection.start.row;
6713 let mut end_row = selection.end.row;
6714
6715 // Skip selections that overlap with a range that has already been rewrapped.
6716 let selection_range = start_row..end_row;
6717 if rewrapped_row_ranges
6718 .iter()
6719 .any(|range| range.overlaps(&selection_range))
6720 {
6721 continue;
6722 }
6723
6724 let mut should_rewrap = is_vim_mode == IsVimMode::Yes;
6725
6726 if let Some(language_scope) = buffer.language_scope_at(selection.head()) {
6727 match language_scope.language_name().0.as_ref() {
6728 "Markdown" | "Plain Text" => {
6729 should_rewrap = true;
6730 }
6731 _ => {}
6732 }
6733 }
6734
6735 let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
6736
6737 // Since not all lines in the selection may be at the same indent
6738 // level, choose the indent size that is the most common between all
6739 // of the lines.
6740 //
6741 // If there is a tie, we use the deepest indent.
6742 let (indent_size, indent_end) = {
6743 let mut indent_size_occurrences = HashMap::default();
6744 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
6745
6746 for row in start_row..=end_row {
6747 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
6748 rows_by_indent_size.entry(indent).or_default().push(row);
6749 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
6750 }
6751
6752 let indent_size = indent_size_occurrences
6753 .into_iter()
6754 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
6755 .map(|(indent, _)| indent)
6756 .unwrap_or_default();
6757 let row = rows_by_indent_size[&indent_size][0];
6758 let indent_end = Point::new(row, indent_size.len);
6759
6760 (indent_size, indent_end)
6761 };
6762
6763 let mut line_prefix = indent_size.chars().collect::<String>();
6764
6765 if let Some(comment_prefix) =
6766 buffer
6767 .language_scope_at(selection.head())
6768 .and_then(|language| {
6769 language
6770 .line_comment_prefixes()
6771 .iter()
6772 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
6773 .cloned()
6774 })
6775 {
6776 line_prefix.push_str(&comment_prefix);
6777 should_rewrap = true;
6778 }
6779
6780 if !should_rewrap {
6781 continue;
6782 }
6783
6784 if selection.is_empty() {
6785 'expand_upwards: while start_row > 0 {
6786 let prev_row = start_row - 1;
6787 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
6788 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
6789 {
6790 start_row = prev_row;
6791 } else {
6792 break 'expand_upwards;
6793 }
6794 }
6795
6796 'expand_downwards: while end_row < buffer.max_point().row {
6797 let next_row = end_row + 1;
6798 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
6799 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
6800 {
6801 end_row = next_row;
6802 } else {
6803 break 'expand_downwards;
6804 }
6805 }
6806 }
6807
6808 let start = Point::new(start_row, 0);
6809 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
6810 let selection_text = buffer.text_for_range(start..end).collect::<String>();
6811 let Some(lines_without_prefixes) = selection_text
6812 .lines()
6813 .map(|line| {
6814 line.strip_prefix(&line_prefix)
6815 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
6816 .ok_or_else(|| {
6817 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
6818 })
6819 })
6820 .collect::<Result<Vec<_>, _>>()
6821 .log_err()
6822 else {
6823 continue;
6824 };
6825
6826 let wrap_column = buffer
6827 .settings_at(Point::new(start_row, 0), cx)
6828 .preferred_line_length as usize;
6829 let wrapped_text = wrap_with_prefix(
6830 line_prefix,
6831 lines_without_prefixes.join(" "),
6832 wrap_column,
6833 tab_size,
6834 );
6835
6836 // TODO: should always use char-based diff while still supporting cursor behavior that
6837 // matches vim.
6838 let diff = match is_vim_mode {
6839 IsVimMode::Yes => TextDiff::from_lines(&selection_text, &wrapped_text),
6840 IsVimMode::No => TextDiff::from_chars(&selection_text, &wrapped_text),
6841 };
6842 let mut offset = start.to_offset(&buffer);
6843 let mut moved_since_edit = true;
6844
6845 for change in diff.iter_all_changes() {
6846 let value = change.value();
6847 match change.tag() {
6848 ChangeTag::Equal => {
6849 offset += value.len();
6850 moved_since_edit = true;
6851 }
6852 ChangeTag::Delete => {
6853 let start = buffer.anchor_after(offset);
6854 let end = buffer.anchor_before(offset + value.len());
6855
6856 if moved_since_edit {
6857 edits.push((start..end, String::new()));
6858 } else {
6859 edits.last_mut().unwrap().0.end = end;
6860 }
6861
6862 offset += value.len();
6863 moved_since_edit = false;
6864 }
6865 ChangeTag::Insert => {
6866 if moved_since_edit {
6867 let anchor = buffer.anchor_after(offset);
6868 edits.push((anchor..anchor, value.to_string()));
6869 } else {
6870 edits.last_mut().unwrap().1.push_str(value);
6871 }
6872
6873 moved_since_edit = false;
6874 }
6875 }
6876 }
6877
6878 rewrapped_row_ranges.push(start_row..=end_row);
6879 }
6880
6881 self.buffer
6882 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
6883 }
6884
6885 pub fn cut_common(&mut self, cx: &mut ViewContext<Self>) -> ClipboardItem {
6886 let mut text = String::new();
6887 let buffer = self.buffer.read(cx).snapshot(cx);
6888 let mut selections = self.selections.all::<Point>(cx);
6889 let mut clipboard_selections = Vec::with_capacity(selections.len());
6890 {
6891 let max_point = buffer.max_point();
6892 let mut is_first = true;
6893 for selection in &mut selections {
6894 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6895 if is_entire_line {
6896 selection.start = Point::new(selection.start.row, 0);
6897 if !selection.is_empty() && selection.end.column == 0 {
6898 selection.end = cmp::min(max_point, selection.end);
6899 } else {
6900 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
6901 }
6902 selection.goal = SelectionGoal::None;
6903 }
6904 if is_first {
6905 is_first = false;
6906 } else {
6907 text += "\n";
6908 }
6909 let mut len = 0;
6910 for chunk in buffer.text_for_range(selection.start..selection.end) {
6911 text.push_str(chunk);
6912 len += chunk.len();
6913 }
6914 clipboard_selections.push(ClipboardSelection {
6915 len,
6916 is_entire_line,
6917 first_line_indent: buffer
6918 .indent_size_for_line(MultiBufferRow(selection.start.row))
6919 .len,
6920 });
6921 }
6922 }
6923
6924 self.transact(cx, |this, cx| {
6925 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6926 s.select(selections);
6927 });
6928 this.insert("", cx);
6929 });
6930 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
6931 }
6932
6933 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
6934 let item = self.cut_common(cx);
6935 cx.write_to_clipboard(item);
6936 }
6937
6938 pub fn kill_ring_cut(&mut self, _: &KillRingCut, cx: &mut ViewContext<Self>) {
6939 self.change_selections(None, cx, |s| {
6940 s.move_with(|snapshot, sel| {
6941 if sel.is_empty() {
6942 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
6943 }
6944 });
6945 });
6946 let item = self.cut_common(cx);
6947 cx.set_global(KillRing(item))
6948 }
6949
6950 pub fn kill_ring_yank(&mut self, _: &KillRingYank, cx: &mut ViewContext<Self>) {
6951 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
6952 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
6953 (kill_ring.text().to_string(), kill_ring.metadata_json())
6954 } else {
6955 return;
6956 }
6957 } else {
6958 return;
6959 };
6960 self.do_paste(&text, metadata, false, cx);
6961 }
6962
6963 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
6964 let selections = self.selections.all::<Point>(cx);
6965 let buffer = self.buffer.read(cx).read(cx);
6966 let mut text = String::new();
6967
6968 let mut clipboard_selections = Vec::with_capacity(selections.len());
6969 {
6970 let max_point = buffer.max_point();
6971 let mut is_first = true;
6972 for selection in selections.iter() {
6973 let mut start = selection.start;
6974 let mut end = selection.end;
6975 let is_entire_line = selection.is_empty() || self.selections.line_mode;
6976 if is_entire_line {
6977 start = Point::new(start.row, 0);
6978 end = cmp::min(max_point, Point::new(end.row + 1, 0));
6979 }
6980 if is_first {
6981 is_first = false;
6982 } else {
6983 text += "\n";
6984 }
6985 let mut len = 0;
6986 for chunk in buffer.text_for_range(start..end) {
6987 text.push_str(chunk);
6988 len += chunk.len();
6989 }
6990 clipboard_selections.push(ClipboardSelection {
6991 len,
6992 is_entire_line,
6993 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
6994 });
6995 }
6996 }
6997
6998 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
6999 text,
7000 clipboard_selections,
7001 ));
7002 }
7003
7004 pub fn do_paste(
7005 &mut self,
7006 text: &String,
7007 clipboard_selections: Option<Vec<ClipboardSelection>>,
7008 handle_entire_lines: bool,
7009 cx: &mut ViewContext<Self>,
7010 ) {
7011 if self.read_only(cx) {
7012 return;
7013 }
7014
7015 let clipboard_text = Cow::Borrowed(text);
7016
7017 self.transact(cx, |this, cx| {
7018 if let Some(mut clipboard_selections) = clipboard_selections {
7019 let old_selections = this.selections.all::<usize>(cx);
7020 let all_selections_were_entire_line =
7021 clipboard_selections.iter().all(|s| s.is_entire_line);
7022 let first_selection_indent_column =
7023 clipboard_selections.first().map(|s| s.first_line_indent);
7024 if clipboard_selections.len() != old_selections.len() {
7025 clipboard_selections.drain(..);
7026 }
7027 let cursor_offset = this.selections.last::<usize>(cx).head();
7028 let mut auto_indent_on_paste = true;
7029
7030 this.buffer.update(cx, |buffer, cx| {
7031 let snapshot = buffer.read(cx);
7032 auto_indent_on_paste =
7033 snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste;
7034
7035 let mut start_offset = 0;
7036 let mut edits = Vec::new();
7037 let mut original_indent_columns = Vec::new();
7038 for (ix, selection) in old_selections.iter().enumerate() {
7039 let to_insert;
7040 let entire_line;
7041 let original_indent_column;
7042 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
7043 let end_offset = start_offset + clipboard_selection.len;
7044 to_insert = &clipboard_text[start_offset..end_offset];
7045 entire_line = clipboard_selection.is_entire_line;
7046 start_offset = end_offset + 1;
7047 original_indent_column = Some(clipboard_selection.first_line_indent);
7048 } else {
7049 to_insert = clipboard_text.as_str();
7050 entire_line = all_selections_were_entire_line;
7051 original_indent_column = first_selection_indent_column
7052 }
7053
7054 // If the corresponding selection was empty when this slice of the
7055 // clipboard text was written, then the entire line containing the
7056 // selection was copied. If this selection is also currently empty,
7057 // then paste the line before the current line of the buffer.
7058 let range = if selection.is_empty() && handle_entire_lines && entire_line {
7059 let column = selection.start.to_point(&snapshot).column as usize;
7060 let line_start = selection.start - column;
7061 line_start..line_start
7062 } else {
7063 selection.range()
7064 };
7065
7066 edits.push((range, to_insert));
7067 original_indent_columns.extend(original_indent_column);
7068 }
7069 drop(snapshot);
7070
7071 buffer.edit(
7072 edits,
7073 if auto_indent_on_paste {
7074 Some(AutoindentMode::Block {
7075 original_indent_columns,
7076 })
7077 } else {
7078 None
7079 },
7080 cx,
7081 );
7082 });
7083
7084 let selections = this.selections.all::<usize>(cx);
7085 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
7086 } else {
7087 this.insert(&clipboard_text, cx);
7088 }
7089 });
7090 }
7091
7092 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
7093 if let Some(item) = cx.read_from_clipboard() {
7094 let entries = item.entries();
7095
7096 match entries.first() {
7097 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
7098 // of all the pasted entries.
7099 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
7100 .do_paste(
7101 clipboard_string.text(),
7102 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
7103 true,
7104 cx,
7105 ),
7106 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, cx),
7107 }
7108 }
7109 }
7110
7111 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
7112 if self.read_only(cx) {
7113 return;
7114 }
7115
7116 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
7117 if let Some((selections, _)) =
7118 self.selection_history.transaction(transaction_id).cloned()
7119 {
7120 self.change_selections(None, cx, |s| {
7121 s.select_anchors(selections.to_vec());
7122 });
7123 }
7124 self.request_autoscroll(Autoscroll::fit(), cx);
7125 self.unmark_text(cx);
7126 self.refresh_inline_completion(true, false, cx);
7127 cx.emit(EditorEvent::Edited { transaction_id });
7128 cx.emit(EditorEvent::TransactionUndone { transaction_id });
7129 }
7130 }
7131
7132 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
7133 if self.read_only(cx) {
7134 return;
7135 }
7136
7137 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
7138 if let Some((_, Some(selections))) =
7139 self.selection_history.transaction(transaction_id).cloned()
7140 {
7141 self.change_selections(None, cx, |s| {
7142 s.select_anchors(selections.to_vec());
7143 });
7144 }
7145 self.request_autoscroll(Autoscroll::fit(), cx);
7146 self.unmark_text(cx);
7147 self.refresh_inline_completion(true, false, cx);
7148 cx.emit(EditorEvent::Edited { transaction_id });
7149 }
7150 }
7151
7152 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
7153 self.buffer
7154 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
7155 }
7156
7157 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut ViewContext<Self>) {
7158 self.buffer
7159 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
7160 }
7161
7162 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
7163 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7164 let line_mode = s.line_mode;
7165 s.move_with(|map, selection| {
7166 let cursor = if selection.is_empty() && !line_mode {
7167 movement::left(map, selection.start)
7168 } else {
7169 selection.start
7170 };
7171 selection.collapse_to(cursor, SelectionGoal::None);
7172 });
7173 })
7174 }
7175
7176 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
7177 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7178 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
7179 })
7180 }
7181
7182 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
7183 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7184 let line_mode = s.line_mode;
7185 s.move_with(|map, selection| {
7186 let cursor = if selection.is_empty() && !line_mode {
7187 movement::right(map, selection.end)
7188 } else {
7189 selection.end
7190 };
7191 selection.collapse_to(cursor, SelectionGoal::None)
7192 });
7193 })
7194 }
7195
7196 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
7197 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7198 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
7199 })
7200 }
7201
7202 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
7203 if self.take_rename(true, cx).is_some() {
7204 return;
7205 }
7206
7207 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7208 cx.propagate();
7209 return;
7210 }
7211
7212 let text_layout_details = &self.text_layout_details(cx);
7213 let selection_count = self.selections.count();
7214 let first_selection = self.selections.first_anchor();
7215
7216 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7217 let line_mode = s.line_mode;
7218 s.move_with(|map, selection| {
7219 if !selection.is_empty() && !line_mode {
7220 selection.goal = SelectionGoal::None;
7221 }
7222 let (cursor, goal) = movement::up(
7223 map,
7224 selection.start,
7225 selection.goal,
7226 false,
7227 text_layout_details,
7228 );
7229 selection.collapse_to(cursor, goal);
7230 });
7231 });
7232
7233 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7234 {
7235 cx.propagate();
7236 }
7237 }
7238
7239 pub fn move_up_by_lines(&mut self, action: &MoveUpByLines, cx: &mut ViewContext<Self>) {
7240 if self.take_rename(true, cx).is_some() {
7241 return;
7242 }
7243
7244 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7245 cx.propagate();
7246 return;
7247 }
7248
7249 let text_layout_details = &self.text_layout_details(cx);
7250
7251 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7252 let line_mode = s.line_mode;
7253 s.move_with(|map, selection| {
7254 if !selection.is_empty() && !line_mode {
7255 selection.goal = SelectionGoal::None;
7256 }
7257 let (cursor, goal) = movement::up_by_rows(
7258 map,
7259 selection.start,
7260 action.lines,
7261 selection.goal,
7262 false,
7263 text_layout_details,
7264 );
7265 selection.collapse_to(cursor, goal);
7266 });
7267 })
7268 }
7269
7270 pub fn move_down_by_lines(&mut self, action: &MoveDownByLines, cx: &mut ViewContext<Self>) {
7271 if self.take_rename(true, cx).is_some() {
7272 return;
7273 }
7274
7275 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7276 cx.propagate();
7277 return;
7278 }
7279
7280 let text_layout_details = &self.text_layout_details(cx);
7281
7282 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7283 let line_mode = s.line_mode;
7284 s.move_with(|map, selection| {
7285 if !selection.is_empty() && !line_mode {
7286 selection.goal = SelectionGoal::None;
7287 }
7288 let (cursor, goal) = movement::down_by_rows(
7289 map,
7290 selection.start,
7291 action.lines,
7292 selection.goal,
7293 false,
7294 text_layout_details,
7295 );
7296 selection.collapse_to(cursor, goal);
7297 });
7298 })
7299 }
7300
7301 pub fn select_down_by_lines(&mut self, action: &SelectDownByLines, cx: &mut ViewContext<Self>) {
7302 let text_layout_details = &self.text_layout_details(cx);
7303 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7304 s.move_heads_with(|map, head, goal| {
7305 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
7306 })
7307 })
7308 }
7309
7310 pub fn select_up_by_lines(&mut self, action: &SelectUpByLines, cx: &mut ViewContext<Self>) {
7311 let text_layout_details = &self.text_layout_details(cx);
7312 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7313 s.move_heads_with(|map, head, goal| {
7314 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
7315 })
7316 })
7317 }
7318
7319 pub fn select_page_up(&mut self, _: &SelectPageUp, cx: &mut ViewContext<Self>) {
7320 let Some(row_count) = self.visible_row_count() else {
7321 return;
7322 };
7323
7324 let text_layout_details = &self.text_layout_details(cx);
7325
7326 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7327 s.move_heads_with(|map, head, goal| {
7328 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
7329 })
7330 })
7331 }
7332
7333 pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext<Self>) {
7334 if self.take_rename(true, cx).is_some() {
7335 return;
7336 }
7337
7338 if self
7339 .context_menu
7340 .borrow_mut()
7341 .as_mut()
7342 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
7343 .unwrap_or(false)
7344 {
7345 return;
7346 }
7347
7348 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7349 cx.propagate();
7350 return;
7351 }
7352
7353 let Some(row_count) = self.visible_row_count() else {
7354 return;
7355 };
7356
7357 let autoscroll = if action.center_cursor {
7358 Autoscroll::center()
7359 } else {
7360 Autoscroll::fit()
7361 };
7362
7363 let text_layout_details = &self.text_layout_details(cx);
7364
7365 self.change_selections(Some(autoscroll), cx, |s| {
7366 let line_mode = s.line_mode;
7367 s.move_with(|map, selection| {
7368 if !selection.is_empty() && !line_mode {
7369 selection.goal = SelectionGoal::None;
7370 }
7371 let (cursor, goal) = movement::up_by_rows(
7372 map,
7373 selection.end,
7374 row_count,
7375 selection.goal,
7376 false,
7377 text_layout_details,
7378 );
7379 selection.collapse_to(cursor, goal);
7380 });
7381 });
7382 }
7383
7384 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
7385 let text_layout_details = &self.text_layout_details(cx);
7386 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7387 s.move_heads_with(|map, head, goal| {
7388 movement::up(map, head, goal, false, text_layout_details)
7389 })
7390 })
7391 }
7392
7393 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
7394 self.take_rename(true, cx);
7395
7396 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7397 cx.propagate();
7398 return;
7399 }
7400
7401 let text_layout_details = &self.text_layout_details(cx);
7402 let selection_count = self.selections.count();
7403 let first_selection = self.selections.first_anchor();
7404
7405 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7406 let line_mode = s.line_mode;
7407 s.move_with(|map, selection| {
7408 if !selection.is_empty() && !line_mode {
7409 selection.goal = SelectionGoal::None;
7410 }
7411 let (cursor, goal) = movement::down(
7412 map,
7413 selection.end,
7414 selection.goal,
7415 false,
7416 text_layout_details,
7417 );
7418 selection.collapse_to(cursor, goal);
7419 });
7420 });
7421
7422 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
7423 {
7424 cx.propagate();
7425 }
7426 }
7427
7428 pub fn select_page_down(&mut self, _: &SelectPageDown, cx: &mut ViewContext<Self>) {
7429 let Some(row_count) = self.visible_row_count() else {
7430 return;
7431 };
7432
7433 let text_layout_details = &self.text_layout_details(cx);
7434
7435 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7436 s.move_heads_with(|map, head, goal| {
7437 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
7438 })
7439 })
7440 }
7441
7442 pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext<Self>) {
7443 if self.take_rename(true, cx).is_some() {
7444 return;
7445 }
7446
7447 if self
7448 .context_menu
7449 .borrow_mut()
7450 .as_mut()
7451 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
7452 .unwrap_or(false)
7453 {
7454 return;
7455 }
7456
7457 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7458 cx.propagate();
7459 return;
7460 }
7461
7462 let Some(row_count) = self.visible_row_count() else {
7463 return;
7464 };
7465
7466 let autoscroll = if action.center_cursor {
7467 Autoscroll::center()
7468 } else {
7469 Autoscroll::fit()
7470 };
7471
7472 let text_layout_details = &self.text_layout_details(cx);
7473 self.change_selections(Some(autoscroll), cx, |s| {
7474 let line_mode = s.line_mode;
7475 s.move_with(|map, selection| {
7476 if !selection.is_empty() && !line_mode {
7477 selection.goal = SelectionGoal::None;
7478 }
7479 let (cursor, goal) = movement::down_by_rows(
7480 map,
7481 selection.end,
7482 row_count,
7483 selection.goal,
7484 false,
7485 text_layout_details,
7486 );
7487 selection.collapse_to(cursor, goal);
7488 });
7489 });
7490 }
7491
7492 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
7493 let text_layout_details = &self.text_layout_details(cx);
7494 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7495 s.move_heads_with(|map, head, goal| {
7496 movement::down(map, head, goal, false, text_layout_details)
7497 })
7498 });
7499 }
7500
7501 pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
7502 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7503 context_menu.select_first(self.completion_provider.as_deref(), cx);
7504 }
7505 }
7506
7507 pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
7508 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7509 context_menu.select_prev(self.completion_provider.as_deref(), cx);
7510 }
7511 }
7512
7513 pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
7514 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7515 context_menu.select_next(self.completion_provider.as_deref(), cx);
7516 }
7517 }
7518
7519 pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
7520 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
7521 context_menu.select_last(self.completion_provider.as_deref(), cx);
7522 }
7523 }
7524
7525 pub fn move_to_previous_word_start(
7526 &mut self,
7527 _: &MoveToPreviousWordStart,
7528 cx: &mut ViewContext<Self>,
7529 ) {
7530 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7531 s.move_cursors_with(|map, head, _| {
7532 (
7533 movement::previous_word_start(map, head),
7534 SelectionGoal::None,
7535 )
7536 });
7537 })
7538 }
7539
7540 pub fn move_to_previous_subword_start(
7541 &mut self,
7542 _: &MoveToPreviousSubwordStart,
7543 cx: &mut ViewContext<Self>,
7544 ) {
7545 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7546 s.move_cursors_with(|map, head, _| {
7547 (
7548 movement::previous_subword_start(map, head),
7549 SelectionGoal::None,
7550 )
7551 });
7552 })
7553 }
7554
7555 pub fn select_to_previous_word_start(
7556 &mut self,
7557 _: &SelectToPreviousWordStart,
7558 cx: &mut ViewContext<Self>,
7559 ) {
7560 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7561 s.move_heads_with(|map, head, _| {
7562 (
7563 movement::previous_word_start(map, head),
7564 SelectionGoal::None,
7565 )
7566 });
7567 })
7568 }
7569
7570 pub fn select_to_previous_subword_start(
7571 &mut self,
7572 _: &SelectToPreviousSubwordStart,
7573 cx: &mut ViewContext<Self>,
7574 ) {
7575 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7576 s.move_heads_with(|map, head, _| {
7577 (
7578 movement::previous_subword_start(map, head),
7579 SelectionGoal::None,
7580 )
7581 });
7582 })
7583 }
7584
7585 pub fn delete_to_previous_word_start(
7586 &mut self,
7587 action: &DeleteToPreviousWordStart,
7588 cx: &mut ViewContext<Self>,
7589 ) {
7590 self.transact(cx, |this, cx| {
7591 this.select_autoclose_pair(cx);
7592 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7593 let line_mode = s.line_mode;
7594 s.move_with(|map, selection| {
7595 if selection.is_empty() && !line_mode {
7596 let cursor = if action.ignore_newlines {
7597 movement::previous_word_start(map, selection.head())
7598 } else {
7599 movement::previous_word_start_or_newline(map, selection.head())
7600 };
7601 selection.set_head(cursor, SelectionGoal::None);
7602 }
7603 });
7604 });
7605 this.insert("", cx);
7606 });
7607 }
7608
7609 pub fn delete_to_previous_subword_start(
7610 &mut self,
7611 _: &DeleteToPreviousSubwordStart,
7612 cx: &mut ViewContext<Self>,
7613 ) {
7614 self.transact(cx, |this, cx| {
7615 this.select_autoclose_pair(cx);
7616 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7617 let line_mode = s.line_mode;
7618 s.move_with(|map, selection| {
7619 if selection.is_empty() && !line_mode {
7620 let cursor = movement::previous_subword_start(map, selection.head());
7621 selection.set_head(cursor, SelectionGoal::None);
7622 }
7623 });
7624 });
7625 this.insert("", cx);
7626 });
7627 }
7628
7629 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
7630 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7631 s.move_cursors_with(|map, head, _| {
7632 (movement::next_word_end(map, head), SelectionGoal::None)
7633 });
7634 })
7635 }
7636
7637 pub fn move_to_next_subword_end(
7638 &mut self,
7639 _: &MoveToNextSubwordEnd,
7640 cx: &mut ViewContext<Self>,
7641 ) {
7642 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7643 s.move_cursors_with(|map, head, _| {
7644 (movement::next_subword_end(map, head), SelectionGoal::None)
7645 });
7646 })
7647 }
7648
7649 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
7650 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7651 s.move_heads_with(|map, head, _| {
7652 (movement::next_word_end(map, head), SelectionGoal::None)
7653 });
7654 })
7655 }
7656
7657 pub fn select_to_next_subword_end(
7658 &mut self,
7659 _: &SelectToNextSubwordEnd,
7660 cx: &mut ViewContext<Self>,
7661 ) {
7662 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7663 s.move_heads_with(|map, head, _| {
7664 (movement::next_subword_end(map, head), SelectionGoal::None)
7665 });
7666 })
7667 }
7668
7669 pub fn delete_to_next_word_end(
7670 &mut self,
7671 action: &DeleteToNextWordEnd,
7672 cx: &mut ViewContext<Self>,
7673 ) {
7674 self.transact(cx, |this, cx| {
7675 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7676 let line_mode = s.line_mode;
7677 s.move_with(|map, selection| {
7678 if selection.is_empty() && !line_mode {
7679 let cursor = if action.ignore_newlines {
7680 movement::next_word_end(map, selection.head())
7681 } else {
7682 movement::next_word_end_or_newline(map, selection.head())
7683 };
7684 selection.set_head(cursor, SelectionGoal::None);
7685 }
7686 });
7687 });
7688 this.insert("", cx);
7689 });
7690 }
7691
7692 pub fn delete_to_next_subword_end(
7693 &mut self,
7694 _: &DeleteToNextSubwordEnd,
7695 cx: &mut ViewContext<Self>,
7696 ) {
7697 self.transact(cx, |this, cx| {
7698 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7699 s.move_with(|map, selection| {
7700 if selection.is_empty() {
7701 let cursor = movement::next_subword_end(map, selection.head());
7702 selection.set_head(cursor, SelectionGoal::None);
7703 }
7704 });
7705 });
7706 this.insert("", cx);
7707 });
7708 }
7709
7710 pub fn move_to_beginning_of_line(
7711 &mut self,
7712 action: &MoveToBeginningOfLine,
7713 cx: &mut ViewContext<Self>,
7714 ) {
7715 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7716 s.move_cursors_with(|map, head, _| {
7717 (
7718 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7719 SelectionGoal::None,
7720 )
7721 });
7722 })
7723 }
7724
7725 pub fn select_to_beginning_of_line(
7726 &mut self,
7727 action: &SelectToBeginningOfLine,
7728 cx: &mut ViewContext<Self>,
7729 ) {
7730 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7731 s.move_heads_with(|map, head, _| {
7732 (
7733 movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
7734 SelectionGoal::None,
7735 )
7736 });
7737 });
7738 }
7739
7740 pub fn delete_to_beginning_of_line(
7741 &mut self,
7742 _: &DeleteToBeginningOfLine,
7743 cx: &mut ViewContext<Self>,
7744 ) {
7745 self.transact(cx, |this, cx| {
7746 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
7747 s.move_with(|_, selection| {
7748 selection.reversed = true;
7749 });
7750 });
7751
7752 this.select_to_beginning_of_line(
7753 &SelectToBeginningOfLine {
7754 stop_at_soft_wraps: false,
7755 },
7756 cx,
7757 );
7758 this.backspace(&Backspace, cx);
7759 });
7760 }
7761
7762 pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
7763 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7764 s.move_cursors_with(|map, head, _| {
7765 (
7766 movement::line_end(map, head, action.stop_at_soft_wraps),
7767 SelectionGoal::None,
7768 )
7769 });
7770 })
7771 }
7772
7773 pub fn select_to_end_of_line(
7774 &mut self,
7775 action: &SelectToEndOfLine,
7776 cx: &mut ViewContext<Self>,
7777 ) {
7778 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7779 s.move_heads_with(|map, head, _| {
7780 (
7781 movement::line_end(map, head, action.stop_at_soft_wraps),
7782 SelectionGoal::None,
7783 )
7784 });
7785 })
7786 }
7787
7788 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
7789 self.transact(cx, |this, cx| {
7790 this.select_to_end_of_line(
7791 &SelectToEndOfLine {
7792 stop_at_soft_wraps: false,
7793 },
7794 cx,
7795 );
7796 this.delete(&Delete, cx);
7797 });
7798 }
7799
7800 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
7801 self.transact(cx, |this, cx| {
7802 this.select_to_end_of_line(
7803 &SelectToEndOfLine {
7804 stop_at_soft_wraps: false,
7805 },
7806 cx,
7807 );
7808 this.cut(&Cut, cx);
7809 });
7810 }
7811
7812 pub fn move_to_start_of_paragraph(
7813 &mut self,
7814 _: &MoveToStartOfParagraph,
7815 cx: &mut ViewContext<Self>,
7816 ) {
7817 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7818 cx.propagate();
7819 return;
7820 }
7821
7822 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7823 s.move_with(|map, selection| {
7824 selection.collapse_to(
7825 movement::start_of_paragraph(map, selection.head(), 1),
7826 SelectionGoal::None,
7827 )
7828 });
7829 })
7830 }
7831
7832 pub fn move_to_end_of_paragraph(
7833 &mut self,
7834 _: &MoveToEndOfParagraph,
7835 cx: &mut ViewContext<Self>,
7836 ) {
7837 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7838 cx.propagate();
7839 return;
7840 }
7841
7842 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7843 s.move_with(|map, selection| {
7844 selection.collapse_to(
7845 movement::end_of_paragraph(map, selection.head(), 1),
7846 SelectionGoal::None,
7847 )
7848 });
7849 })
7850 }
7851
7852 pub fn select_to_start_of_paragraph(
7853 &mut self,
7854 _: &SelectToStartOfParagraph,
7855 cx: &mut ViewContext<Self>,
7856 ) {
7857 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7858 cx.propagate();
7859 return;
7860 }
7861
7862 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7863 s.move_heads_with(|map, head, _| {
7864 (
7865 movement::start_of_paragraph(map, head, 1),
7866 SelectionGoal::None,
7867 )
7868 });
7869 })
7870 }
7871
7872 pub fn select_to_end_of_paragraph(
7873 &mut self,
7874 _: &SelectToEndOfParagraph,
7875 cx: &mut ViewContext<Self>,
7876 ) {
7877 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7878 cx.propagate();
7879 return;
7880 }
7881
7882 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7883 s.move_heads_with(|map, head, _| {
7884 (
7885 movement::end_of_paragraph(map, head, 1),
7886 SelectionGoal::None,
7887 )
7888 });
7889 })
7890 }
7891
7892 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
7893 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7894 cx.propagate();
7895 return;
7896 }
7897
7898 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7899 s.select_ranges(vec![0..0]);
7900 });
7901 }
7902
7903 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
7904 let mut selection = self.selections.last::<Point>(cx);
7905 selection.set_head(Point::zero(), SelectionGoal::None);
7906
7907 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7908 s.select(vec![selection]);
7909 });
7910 }
7911
7912 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
7913 if matches!(self.mode, EditorMode::SingleLine { .. }) {
7914 cx.propagate();
7915 return;
7916 }
7917
7918 let cursor = self.buffer.read(cx).read(cx).len();
7919 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7920 s.select_ranges(vec![cursor..cursor])
7921 });
7922 }
7923
7924 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
7925 self.nav_history = nav_history;
7926 }
7927
7928 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
7929 self.nav_history.as_ref()
7930 }
7931
7932 fn push_to_nav_history(
7933 &mut self,
7934 cursor_anchor: Anchor,
7935 new_position: Option<Point>,
7936 cx: &mut ViewContext<Self>,
7937 ) {
7938 if let Some(nav_history) = self.nav_history.as_mut() {
7939 let buffer = self.buffer.read(cx).read(cx);
7940 let cursor_position = cursor_anchor.to_point(&buffer);
7941 let scroll_state = self.scroll_manager.anchor();
7942 let scroll_top_row = scroll_state.top_row(&buffer);
7943 drop(buffer);
7944
7945 if let Some(new_position) = new_position {
7946 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
7947 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
7948 return;
7949 }
7950 }
7951
7952 nav_history.push(
7953 Some(NavigationData {
7954 cursor_anchor,
7955 cursor_position,
7956 scroll_anchor: scroll_state,
7957 scroll_top_row,
7958 }),
7959 cx,
7960 );
7961 }
7962 }
7963
7964 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
7965 let buffer = self.buffer.read(cx).snapshot(cx);
7966 let mut selection = self.selections.first::<usize>(cx);
7967 selection.set_head(buffer.len(), SelectionGoal::None);
7968 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7969 s.select(vec![selection]);
7970 });
7971 }
7972
7973 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
7974 let end = self.buffer.read(cx).read(cx).len();
7975 self.change_selections(None, cx, |s| {
7976 s.select_ranges(vec![0..end]);
7977 });
7978 }
7979
7980 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
7981 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7982 let mut selections = self.selections.all::<Point>(cx);
7983 let max_point = display_map.buffer_snapshot.max_point();
7984 for selection in &mut selections {
7985 let rows = selection.spanned_rows(true, &display_map);
7986 selection.start = Point::new(rows.start.0, 0);
7987 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
7988 selection.reversed = false;
7989 }
7990 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
7991 s.select(selections);
7992 });
7993 }
7994
7995 pub fn split_selection_into_lines(
7996 &mut self,
7997 _: &SplitSelectionIntoLines,
7998 cx: &mut ViewContext<Self>,
7999 ) {
8000 let mut to_unfold = Vec::new();
8001 let mut new_selection_ranges = Vec::new();
8002 {
8003 let selections = self.selections.all::<Point>(cx);
8004 let buffer = self.buffer.read(cx).read(cx);
8005 for selection in selections {
8006 for row in selection.start.row..selection.end.row {
8007 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
8008 new_selection_ranges.push(cursor..cursor);
8009 }
8010 new_selection_ranges.push(selection.end..selection.end);
8011 to_unfold.push(selection.start..selection.end);
8012 }
8013 }
8014 self.unfold_ranges(&to_unfold, true, true, cx);
8015 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8016 s.select_ranges(new_selection_ranges);
8017 });
8018 }
8019
8020 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
8021 self.add_selection(true, cx);
8022 }
8023
8024 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
8025 self.add_selection(false, cx);
8026 }
8027
8028 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
8029 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8030 let mut selections = self.selections.all::<Point>(cx);
8031 let text_layout_details = self.text_layout_details(cx);
8032 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
8033 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
8034 let range = oldest_selection.display_range(&display_map).sorted();
8035
8036 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
8037 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
8038 let positions = start_x.min(end_x)..start_x.max(end_x);
8039
8040 selections.clear();
8041 let mut stack = Vec::new();
8042 for row in range.start.row().0..=range.end.row().0 {
8043 if let Some(selection) = self.selections.build_columnar_selection(
8044 &display_map,
8045 DisplayRow(row),
8046 &positions,
8047 oldest_selection.reversed,
8048 &text_layout_details,
8049 ) {
8050 stack.push(selection.id);
8051 selections.push(selection);
8052 }
8053 }
8054
8055 if above {
8056 stack.reverse();
8057 }
8058
8059 AddSelectionsState { above, stack }
8060 });
8061
8062 let last_added_selection = *state.stack.last().unwrap();
8063 let mut new_selections = Vec::new();
8064 if above == state.above {
8065 let end_row = if above {
8066 DisplayRow(0)
8067 } else {
8068 display_map.max_point().row()
8069 };
8070
8071 'outer: for selection in selections {
8072 if selection.id == last_added_selection {
8073 let range = selection.display_range(&display_map).sorted();
8074 debug_assert_eq!(range.start.row(), range.end.row());
8075 let mut row = range.start.row();
8076 let positions =
8077 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
8078 px(start)..px(end)
8079 } else {
8080 let start_x =
8081 display_map.x_for_display_point(range.start, &text_layout_details);
8082 let end_x =
8083 display_map.x_for_display_point(range.end, &text_layout_details);
8084 start_x.min(end_x)..start_x.max(end_x)
8085 };
8086
8087 while row != end_row {
8088 if above {
8089 row.0 -= 1;
8090 } else {
8091 row.0 += 1;
8092 }
8093
8094 if let Some(new_selection) = self.selections.build_columnar_selection(
8095 &display_map,
8096 row,
8097 &positions,
8098 selection.reversed,
8099 &text_layout_details,
8100 ) {
8101 state.stack.push(new_selection.id);
8102 if above {
8103 new_selections.push(new_selection);
8104 new_selections.push(selection);
8105 } else {
8106 new_selections.push(selection);
8107 new_selections.push(new_selection);
8108 }
8109
8110 continue 'outer;
8111 }
8112 }
8113 }
8114
8115 new_selections.push(selection);
8116 }
8117 } else {
8118 new_selections = selections;
8119 new_selections.retain(|s| s.id != last_added_selection);
8120 state.stack.pop();
8121 }
8122
8123 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8124 s.select(new_selections);
8125 });
8126 if state.stack.len() > 1 {
8127 self.add_selections_state = Some(state);
8128 }
8129 }
8130
8131 pub fn select_next_match_internal(
8132 &mut self,
8133 display_map: &DisplaySnapshot,
8134 replace_newest: bool,
8135 autoscroll: Option<Autoscroll>,
8136 cx: &mut ViewContext<Self>,
8137 ) -> Result<()> {
8138 fn select_next_match_ranges(
8139 this: &mut Editor,
8140 range: Range<usize>,
8141 replace_newest: bool,
8142 auto_scroll: Option<Autoscroll>,
8143 cx: &mut ViewContext<Editor>,
8144 ) {
8145 this.unfold_ranges(&[range.clone()], false, true, cx);
8146 this.change_selections(auto_scroll, cx, |s| {
8147 if replace_newest {
8148 s.delete(s.newest_anchor().id);
8149 }
8150 s.insert_range(range.clone());
8151 });
8152 }
8153
8154 let buffer = &display_map.buffer_snapshot;
8155 let mut selections = self.selections.all::<usize>(cx);
8156 if let Some(mut select_next_state) = self.select_next_state.take() {
8157 let query = &select_next_state.query;
8158 if !select_next_state.done {
8159 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8160 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8161 let mut next_selected_range = None;
8162
8163 let bytes_after_last_selection =
8164 buffer.bytes_in_range(last_selection.end..buffer.len());
8165 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
8166 let query_matches = query
8167 .stream_find_iter(bytes_after_last_selection)
8168 .map(|result| (last_selection.end, result))
8169 .chain(
8170 query
8171 .stream_find_iter(bytes_before_first_selection)
8172 .map(|result| (0, result)),
8173 );
8174
8175 for (start_offset, query_match) in query_matches {
8176 let query_match = query_match.unwrap(); // can only fail due to I/O
8177 let offset_range =
8178 start_offset + query_match.start()..start_offset + query_match.end();
8179 let display_range = offset_range.start.to_display_point(display_map)
8180 ..offset_range.end.to_display_point(display_map);
8181
8182 if !select_next_state.wordwise
8183 || (!movement::is_inside_word(display_map, display_range.start)
8184 && !movement::is_inside_word(display_map, display_range.end))
8185 {
8186 // TODO: This is n^2, because we might check all the selections
8187 if !selections
8188 .iter()
8189 .any(|selection| selection.range().overlaps(&offset_range))
8190 {
8191 next_selected_range = Some(offset_range);
8192 break;
8193 }
8194 }
8195 }
8196
8197 if let Some(next_selected_range) = next_selected_range {
8198 select_next_match_ranges(
8199 self,
8200 next_selected_range,
8201 replace_newest,
8202 autoscroll,
8203 cx,
8204 );
8205 } else {
8206 select_next_state.done = true;
8207 }
8208 }
8209
8210 self.select_next_state = Some(select_next_state);
8211 } else {
8212 let mut only_carets = true;
8213 let mut same_text_selected = true;
8214 let mut selected_text = None;
8215
8216 let mut selections_iter = selections.iter().peekable();
8217 while let Some(selection) = selections_iter.next() {
8218 if selection.start != selection.end {
8219 only_carets = false;
8220 }
8221
8222 if same_text_selected {
8223 if selected_text.is_none() {
8224 selected_text =
8225 Some(buffer.text_for_range(selection.range()).collect::<String>());
8226 }
8227
8228 if let Some(next_selection) = selections_iter.peek() {
8229 if next_selection.range().len() == selection.range().len() {
8230 let next_selected_text = buffer
8231 .text_for_range(next_selection.range())
8232 .collect::<String>();
8233 if Some(next_selected_text) != selected_text {
8234 same_text_selected = false;
8235 selected_text = None;
8236 }
8237 } else {
8238 same_text_selected = false;
8239 selected_text = None;
8240 }
8241 }
8242 }
8243 }
8244
8245 if only_carets {
8246 for selection in &mut selections {
8247 let word_range = movement::surrounding_word(
8248 display_map,
8249 selection.start.to_display_point(display_map),
8250 );
8251 selection.start = word_range.start.to_offset(display_map, Bias::Left);
8252 selection.end = word_range.end.to_offset(display_map, Bias::Left);
8253 selection.goal = SelectionGoal::None;
8254 selection.reversed = false;
8255 select_next_match_ranges(
8256 self,
8257 selection.start..selection.end,
8258 replace_newest,
8259 autoscroll,
8260 cx,
8261 );
8262 }
8263
8264 if selections.len() == 1 {
8265 let selection = selections
8266 .last()
8267 .expect("ensured that there's only one selection");
8268 let query = buffer
8269 .text_for_range(selection.start..selection.end)
8270 .collect::<String>();
8271 let is_empty = query.is_empty();
8272 let select_state = SelectNextState {
8273 query: AhoCorasick::new(&[query])?,
8274 wordwise: true,
8275 done: is_empty,
8276 };
8277 self.select_next_state = Some(select_state);
8278 } else {
8279 self.select_next_state = None;
8280 }
8281 } else if let Some(selected_text) = selected_text {
8282 self.select_next_state = Some(SelectNextState {
8283 query: AhoCorasick::new(&[selected_text])?,
8284 wordwise: false,
8285 done: false,
8286 });
8287 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
8288 }
8289 }
8290 Ok(())
8291 }
8292
8293 pub fn select_all_matches(
8294 &mut self,
8295 _action: &SelectAllMatches,
8296 cx: &mut ViewContext<Self>,
8297 ) -> Result<()> {
8298 self.push_to_selection_history();
8299 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8300
8301 self.select_next_match_internal(&display_map, false, None, cx)?;
8302 let Some(select_next_state) = self.select_next_state.as_mut() else {
8303 return Ok(());
8304 };
8305 if select_next_state.done {
8306 return Ok(());
8307 }
8308
8309 let mut new_selections = self.selections.all::<usize>(cx);
8310
8311 let buffer = &display_map.buffer_snapshot;
8312 let query_matches = select_next_state
8313 .query
8314 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
8315
8316 for query_match in query_matches {
8317 let query_match = query_match.unwrap(); // can only fail due to I/O
8318 let offset_range = query_match.start()..query_match.end();
8319 let display_range = offset_range.start.to_display_point(&display_map)
8320 ..offset_range.end.to_display_point(&display_map);
8321
8322 if !select_next_state.wordwise
8323 || (!movement::is_inside_word(&display_map, display_range.start)
8324 && !movement::is_inside_word(&display_map, display_range.end))
8325 {
8326 self.selections.change_with(cx, |selections| {
8327 new_selections.push(Selection {
8328 id: selections.new_selection_id(),
8329 start: offset_range.start,
8330 end: offset_range.end,
8331 reversed: false,
8332 goal: SelectionGoal::None,
8333 });
8334 });
8335 }
8336 }
8337
8338 new_selections.sort_by_key(|selection| selection.start);
8339 let mut ix = 0;
8340 while ix + 1 < new_selections.len() {
8341 let current_selection = &new_selections[ix];
8342 let next_selection = &new_selections[ix + 1];
8343 if current_selection.range().overlaps(&next_selection.range()) {
8344 if current_selection.id < next_selection.id {
8345 new_selections.remove(ix + 1);
8346 } else {
8347 new_selections.remove(ix);
8348 }
8349 } else {
8350 ix += 1;
8351 }
8352 }
8353
8354 select_next_state.done = true;
8355 self.unfold_ranges(
8356 &new_selections
8357 .iter()
8358 .map(|selection| selection.range())
8359 .collect::<Vec<_>>(),
8360 false,
8361 false,
8362 cx,
8363 );
8364 self.change_selections(Some(Autoscroll::fit()), cx, |selections| {
8365 selections.select(new_selections)
8366 });
8367
8368 Ok(())
8369 }
8370
8371 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
8372 self.push_to_selection_history();
8373 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8374 self.select_next_match_internal(
8375 &display_map,
8376 action.replace_newest,
8377 Some(Autoscroll::newest()),
8378 cx,
8379 )?;
8380 Ok(())
8381 }
8382
8383 pub fn select_previous(
8384 &mut self,
8385 action: &SelectPrevious,
8386 cx: &mut ViewContext<Self>,
8387 ) -> Result<()> {
8388 self.push_to_selection_history();
8389 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8390 let buffer = &display_map.buffer_snapshot;
8391 let mut selections = self.selections.all::<usize>(cx);
8392 if let Some(mut select_prev_state) = self.select_prev_state.take() {
8393 let query = &select_prev_state.query;
8394 if !select_prev_state.done {
8395 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
8396 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
8397 let mut next_selected_range = None;
8398 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
8399 let bytes_before_last_selection =
8400 buffer.reversed_bytes_in_range(0..last_selection.start);
8401 let bytes_after_first_selection =
8402 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
8403 let query_matches = query
8404 .stream_find_iter(bytes_before_last_selection)
8405 .map(|result| (last_selection.start, result))
8406 .chain(
8407 query
8408 .stream_find_iter(bytes_after_first_selection)
8409 .map(|result| (buffer.len(), result)),
8410 );
8411 for (end_offset, query_match) in query_matches {
8412 let query_match = query_match.unwrap(); // can only fail due to I/O
8413 let offset_range =
8414 end_offset - query_match.end()..end_offset - query_match.start();
8415 let display_range = offset_range.start.to_display_point(&display_map)
8416 ..offset_range.end.to_display_point(&display_map);
8417
8418 if !select_prev_state.wordwise
8419 || (!movement::is_inside_word(&display_map, display_range.start)
8420 && !movement::is_inside_word(&display_map, display_range.end))
8421 {
8422 next_selected_range = Some(offset_range);
8423 break;
8424 }
8425 }
8426
8427 if let Some(next_selected_range) = next_selected_range {
8428 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
8429 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8430 if action.replace_newest {
8431 s.delete(s.newest_anchor().id);
8432 }
8433 s.insert_range(next_selected_range);
8434 });
8435 } else {
8436 select_prev_state.done = true;
8437 }
8438 }
8439
8440 self.select_prev_state = Some(select_prev_state);
8441 } else {
8442 let mut only_carets = true;
8443 let mut same_text_selected = true;
8444 let mut selected_text = None;
8445
8446 let mut selections_iter = selections.iter().peekable();
8447 while let Some(selection) = selections_iter.next() {
8448 if selection.start != selection.end {
8449 only_carets = false;
8450 }
8451
8452 if same_text_selected {
8453 if selected_text.is_none() {
8454 selected_text =
8455 Some(buffer.text_for_range(selection.range()).collect::<String>());
8456 }
8457
8458 if let Some(next_selection) = selections_iter.peek() {
8459 if next_selection.range().len() == selection.range().len() {
8460 let next_selected_text = buffer
8461 .text_for_range(next_selection.range())
8462 .collect::<String>();
8463 if Some(next_selected_text) != selected_text {
8464 same_text_selected = false;
8465 selected_text = None;
8466 }
8467 } else {
8468 same_text_selected = false;
8469 selected_text = None;
8470 }
8471 }
8472 }
8473 }
8474
8475 if only_carets {
8476 for selection in &mut selections {
8477 let word_range = movement::surrounding_word(
8478 &display_map,
8479 selection.start.to_display_point(&display_map),
8480 );
8481 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
8482 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
8483 selection.goal = SelectionGoal::None;
8484 selection.reversed = false;
8485 }
8486 if selections.len() == 1 {
8487 let selection = selections
8488 .last()
8489 .expect("ensured that there's only one selection");
8490 let query = buffer
8491 .text_for_range(selection.start..selection.end)
8492 .collect::<String>();
8493 let is_empty = query.is_empty();
8494 let select_state = SelectNextState {
8495 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
8496 wordwise: true,
8497 done: is_empty,
8498 };
8499 self.select_prev_state = Some(select_state);
8500 } else {
8501 self.select_prev_state = None;
8502 }
8503
8504 self.unfold_ranges(
8505 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
8506 false,
8507 true,
8508 cx,
8509 );
8510 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
8511 s.select(selections);
8512 });
8513 } else if let Some(selected_text) = selected_text {
8514 self.select_prev_state = Some(SelectNextState {
8515 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
8516 wordwise: false,
8517 done: false,
8518 });
8519 self.select_previous(action, cx)?;
8520 }
8521 }
8522 Ok(())
8523 }
8524
8525 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
8526 if self.read_only(cx) {
8527 return;
8528 }
8529 let text_layout_details = &self.text_layout_details(cx);
8530 self.transact(cx, |this, cx| {
8531 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
8532 let mut edits = Vec::new();
8533 let mut selection_edit_ranges = Vec::new();
8534 let mut last_toggled_row = None;
8535 let snapshot = this.buffer.read(cx).read(cx);
8536 let empty_str: Arc<str> = Arc::default();
8537 let mut suffixes_inserted = Vec::new();
8538 let ignore_indent = action.ignore_indent;
8539
8540 fn comment_prefix_range(
8541 snapshot: &MultiBufferSnapshot,
8542 row: MultiBufferRow,
8543 comment_prefix: &str,
8544 comment_prefix_whitespace: &str,
8545 ignore_indent: bool,
8546 ) -> Range<Point> {
8547 let indent_size = if ignore_indent {
8548 0
8549 } else {
8550 snapshot.indent_size_for_line(row).len
8551 };
8552
8553 let start = Point::new(row.0, indent_size);
8554
8555 let mut line_bytes = snapshot
8556 .bytes_in_range(start..snapshot.max_point())
8557 .flatten()
8558 .copied();
8559
8560 // If this line currently begins with the line comment prefix, then record
8561 // the range containing the prefix.
8562 if line_bytes
8563 .by_ref()
8564 .take(comment_prefix.len())
8565 .eq(comment_prefix.bytes())
8566 {
8567 // Include any whitespace that matches the comment prefix.
8568 let matching_whitespace_len = line_bytes
8569 .zip(comment_prefix_whitespace.bytes())
8570 .take_while(|(a, b)| a == b)
8571 .count() as u32;
8572 let end = Point::new(
8573 start.row,
8574 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
8575 );
8576 start..end
8577 } else {
8578 start..start
8579 }
8580 }
8581
8582 fn comment_suffix_range(
8583 snapshot: &MultiBufferSnapshot,
8584 row: MultiBufferRow,
8585 comment_suffix: &str,
8586 comment_suffix_has_leading_space: bool,
8587 ) -> Range<Point> {
8588 let end = Point::new(row.0, snapshot.line_len(row));
8589 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
8590
8591 let mut line_end_bytes = snapshot
8592 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
8593 .flatten()
8594 .copied();
8595
8596 let leading_space_len = if suffix_start_column > 0
8597 && line_end_bytes.next() == Some(b' ')
8598 && comment_suffix_has_leading_space
8599 {
8600 1
8601 } else {
8602 0
8603 };
8604
8605 // If this line currently begins with the line comment prefix, then record
8606 // the range containing the prefix.
8607 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
8608 let start = Point::new(end.row, suffix_start_column - leading_space_len);
8609 start..end
8610 } else {
8611 end..end
8612 }
8613 }
8614
8615 // TODO: Handle selections that cross excerpts
8616 for selection in &mut selections {
8617 let start_column = snapshot
8618 .indent_size_for_line(MultiBufferRow(selection.start.row))
8619 .len;
8620 let language = if let Some(language) =
8621 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
8622 {
8623 language
8624 } else {
8625 continue;
8626 };
8627
8628 selection_edit_ranges.clear();
8629
8630 // If multiple selections contain a given row, avoid processing that
8631 // row more than once.
8632 let mut start_row = MultiBufferRow(selection.start.row);
8633 if last_toggled_row == Some(start_row) {
8634 start_row = start_row.next_row();
8635 }
8636 let end_row =
8637 if selection.end.row > selection.start.row && selection.end.column == 0 {
8638 MultiBufferRow(selection.end.row - 1)
8639 } else {
8640 MultiBufferRow(selection.end.row)
8641 };
8642 last_toggled_row = Some(end_row);
8643
8644 if start_row > end_row {
8645 continue;
8646 }
8647
8648 // If the language has line comments, toggle those.
8649 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
8650
8651 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
8652 if ignore_indent {
8653 full_comment_prefixes = full_comment_prefixes
8654 .into_iter()
8655 .map(|s| Arc::from(s.trim_end()))
8656 .collect();
8657 }
8658
8659 if !full_comment_prefixes.is_empty() {
8660 let first_prefix = full_comment_prefixes
8661 .first()
8662 .expect("prefixes is non-empty");
8663 let prefix_trimmed_lengths = full_comment_prefixes
8664 .iter()
8665 .map(|p| p.trim_end_matches(' ').len())
8666 .collect::<SmallVec<[usize; 4]>>();
8667
8668 let mut all_selection_lines_are_comments = true;
8669
8670 for row in start_row.0..=end_row.0 {
8671 let row = MultiBufferRow(row);
8672 if start_row < end_row && snapshot.is_line_blank(row) {
8673 continue;
8674 }
8675
8676 let prefix_range = full_comment_prefixes
8677 .iter()
8678 .zip(prefix_trimmed_lengths.iter().copied())
8679 .map(|(prefix, trimmed_prefix_len)| {
8680 comment_prefix_range(
8681 snapshot.deref(),
8682 row,
8683 &prefix[..trimmed_prefix_len],
8684 &prefix[trimmed_prefix_len..],
8685 ignore_indent,
8686 )
8687 })
8688 .max_by_key(|range| range.end.column - range.start.column)
8689 .expect("prefixes is non-empty");
8690
8691 if prefix_range.is_empty() {
8692 all_selection_lines_are_comments = false;
8693 }
8694
8695 selection_edit_ranges.push(prefix_range);
8696 }
8697
8698 if all_selection_lines_are_comments {
8699 edits.extend(
8700 selection_edit_ranges
8701 .iter()
8702 .cloned()
8703 .map(|range| (range, empty_str.clone())),
8704 );
8705 } else {
8706 let min_column = selection_edit_ranges
8707 .iter()
8708 .map(|range| range.start.column)
8709 .min()
8710 .unwrap_or(0);
8711 edits.extend(selection_edit_ranges.iter().map(|range| {
8712 let position = Point::new(range.start.row, min_column);
8713 (position..position, first_prefix.clone())
8714 }));
8715 }
8716 } else if let Some((full_comment_prefix, comment_suffix)) =
8717 language.block_comment_delimiters()
8718 {
8719 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
8720 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
8721 let prefix_range = comment_prefix_range(
8722 snapshot.deref(),
8723 start_row,
8724 comment_prefix,
8725 comment_prefix_whitespace,
8726 ignore_indent,
8727 );
8728 let suffix_range = comment_suffix_range(
8729 snapshot.deref(),
8730 end_row,
8731 comment_suffix.trim_start_matches(' '),
8732 comment_suffix.starts_with(' '),
8733 );
8734
8735 if prefix_range.is_empty() || suffix_range.is_empty() {
8736 edits.push((
8737 prefix_range.start..prefix_range.start,
8738 full_comment_prefix.clone(),
8739 ));
8740 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
8741 suffixes_inserted.push((end_row, comment_suffix.len()));
8742 } else {
8743 edits.push((prefix_range, empty_str.clone()));
8744 edits.push((suffix_range, empty_str.clone()));
8745 }
8746 } else {
8747 continue;
8748 }
8749 }
8750
8751 drop(snapshot);
8752 this.buffer.update(cx, |buffer, cx| {
8753 buffer.edit(edits, None, cx);
8754 });
8755
8756 // Adjust selections so that they end before any comment suffixes that
8757 // were inserted.
8758 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
8759 let mut selections = this.selections.all::<Point>(cx);
8760 let snapshot = this.buffer.read(cx).read(cx);
8761 for selection in &mut selections {
8762 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
8763 match row.cmp(&MultiBufferRow(selection.end.row)) {
8764 Ordering::Less => {
8765 suffixes_inserted.next();
8766 continue;
8767 }
8768 Ordering::Greater => break,
8769 Ordering::Equal => {
8770 if selection.end.column == snapshot.line_len(row) {
8771 if selection.is_empty() {
8772 selection.start.column -= suffix_len as u32;
8773 }
8774 selection.end.column -= suffix_len as u32;
8775 }
8776 break;
8777 }
8778 }
8779 }
8780 }
8781
8782 drop(snapshot);
8783 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
8784
8785 let selections = this.selections.all::<Point>(cx);
8786 let selections_on_single_row = selections.windows(2).all(|selections| {
8787 selections[0].start.row == selections[1].start.row
8788 && selections[0].end.row == selections[1].end.row
8789 && selections[0].start.row == selections[0].end.row
8790 });
8791 let selections_selecting = selections
8792 .iter()
8793 .any(|selection| selection.start != selection.end);
8794 let advance_downwards = action.advance_downwards
8795 && selections_on_single_row
8796 && !selections_selecting
8797 && !matches!(this.mode, EditorMode::SingleLine { .. });
8798
8799 if advance_downwards {
8800 let snapshot = this.buffer.read(cx).snapshot(cx);
8801
8802 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
8803 s.move_cursors_with(|display_snapshot, display_point, _| {
8804 let mut point = display_point.to_point(display_snapshot);
8805 point.row += 1;
8806 point = snapshot.clip_point(point, Bias::Left);
8807 let display_point = point.to_display_point(display_snapshot);
8808 let goal = SelectionGoal::HorizontalPosition(
8809 display_snapshot
8810 .x_for_display_point(display_point, text_layout_details)
8811 .into(),
8812 );
8813 (display_point, goal)
8814 })
8815 });
8816 }
8817 });
8818 }
8819
8820 pub fn select_enclosing_symbol(
8821 &mut self,
8822 _: &SelectEnclosingSymbol,
8823 cx: &mut ViewContext<Self>,
8824 ) {
8825 let buffer = self.buffer.read(cx).snapshot(cx);
8826 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8827
8828 fn update_selection(
8829 selection: &Selection<usize>,
8830 buffer_snap: &MultiBufferSnapshot,
8831 ) -> Option<Selection<usize>> {
8832 let cursor = selection.head();
8833 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
8834 for symbol in symbols.iter().rev() {
8835 let start = symbol.range.start.to_offset(buffer_snap);
8836 let end = symbol.range.end.to_offset(buffer_snap);
8837 let new_range = start..end;
8838 if start < selection.start || end > selection.end {
8839 return Some(Selection {
8840 id: selection.id,
8841 start: new_range.start,
8842 end: new_range.end,
8843 goal: SelectionGoal::None,
8844 reversed: selection.reversed,
8845 });
8846 }
8847 }
8848 None
8849 }
8850
8851 let mut selected_larger_symbol = false;
8852 let new_selections = old_selections
8853 .iter()
8854 .map(|selection| match update_selection(selection, &buffer) {
8855 Some(new_selection) => {
8856 if new_selection.range() != selection.range() {
8857 selected_larger_symbol = true;
8858 }
8859 new_selection
8860 }
8861 None => selection.clone(),
8862 })
8863 .collect::<Vec<_>>();
8864
8865 if selected_larger_symbol {
8866 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8867 s.select(new_selections);
8868 });
8869 }
8870 }
8871
8872 pub fn select_larger_syntax_node(
8873 &mut self,
8874 _: &SelectLargerSyntaxNode,
8875 cx: &mut ViewContext<Self>,
8876 ) {
8877 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8878 let buffer = self.buffer.read(cx).snapshot(cx);
8879 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
8880
8881 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8882 let mut selected_larger_node = false;
8883 let new_selections = old_selections
8884 .iter()
8885 .map(|selection| {
8886 let old_range = selection.start..selection.end;
8887 let mut new_range = old_range.clone();
8888 let mut new_node = None;
8889 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
8890 {
8891 new_node = Some(node);
8892 new_range = containing_range;
8893 if !display_map.intersects_fold(new_range.start)
8894 && !display_map.intersects_fold(new_range.end)
8895 {
8896 break;
8897 }
8898 }
8899
8900 if let Some(node) = new_node {
8901 // Log the ancestor, to support using this action as a way to explore TreeSitter
8902 // nodes. Parent and grandparent are also logged because this operation will not
8903 // visit nodes that have the same range as their parent.
8904 log::info!("Node: {node:?}");
8905 let parent = node.parent();
8906 log::info!("Parent: {parent:?}");
8907 let grandparent = parent.and_then(|x| x.parent());
8908 log::info!("Grandparent: {grandparent:?}");
8909 }
8910
8911 selected_larger_node |= new_range != old_range;
8912 Selection {
8913 id: selection.id,
8914 start: new_range.start,
8915 end: new_range.end,
8916 goal: SelectionGoal::None,
8917 reversed: selection.reversed,
8918 }
8919 })
8920 .collect::<Vec<_>>();
8921
8922 if selected_larger_node {
8923 stack.push(old_selections);
8924 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8925 s.select(new_selections);
8926 });
8927 }
8928 self.select_larger_syntax_node_stack = stack;
8929 }
8930
8931 pub fn select_smaller_syntax_node(
8932 &mut self,
8933 _: &SelectSmallerSyntaxNode,
8934 cx: &mut ViewContext<Self>,
8935 ) {
8936 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
8937 if let Some(selections) = stack.pop() {
8938 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
8939 s.select(selections.to_vec());
8940 });
8941 }
8942 self.select_larger_syntax_node_stack = stack;
8943 }
8944
8945 fn refresh_runnables(&mut self, cx: &mut ViewContext<Self>) -> Task<()> {
8946 if !EditorSettings::get_global(cx).gutter.runnables {
8947 self.clear_tasks();
8948 return Task::ready(());
8949 }
8950 let project = self.project.as_ref().map(Model::downgrade);
8951 cx.spawn(|this, mut cx| async move {
8952 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
8953 let Some(project) = project.and_then(|p| p.upgrade()) else {
8954 return;
8955 };
8956 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
8957 this.display_map.update(cx, |map, cx| map.snapshot(cx))
8958 }) else {
8959 return;
8960 };
8961
8962 let hide_runnables = project
8963 .update(&mut cx, |project, cx| {
8964 // Do not display any test indicators in non-dev server remote projects.
8965 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
8966 })
8967 .unwrap_or(true);
8968 if hide_runnables {
8969 return;
8970 }
8971 let new_rows =
8972 cx.background_executor()
8973 .spawn({
8974 let snapshot = display_snapshot.clone();
8975 async move {
8976 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
8977 }
8978 })
8979 .await;
8980 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
8981
8982 this.update(&mut cx, |this, _| {
8983 this.clear_tasks();
8984 for (key, value) in rows {
8985 this.insert_tasks(key, value);
8986 }
8987 })
8988 .ok();
8989 })
8990 }
8991 fn fetch_runnable_ranges(
8992 snapshot: &DisplaySnapshot,
8993 range: Range<Anchor>,
8994 ) -> Vec<language::RunnableRange> {
8995 snapshot.buffer_snapshot.runnable_ranges(range).collect()
8996 }
8997
8998 fn runnable_rows(
8999 project: Model<Project>,
9000 snapshot: DisplaySnapshot,
9001 runnable_ranges: Vec<RunnableRange>,
9002 mut cx: AsyncWindowContext,
9003 ) -> Vec<((BufferId, u32), RunnableTasks)> {
9004 runnable_ranges
9005 .into_iter()
9006 .filter_map(|mut runnable| {
9007 let tasks = cx
9008 .update(|cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
9009 .ok()?;
9010 if tasks.is_empty() {
9011 return None;
9012 }
9013
9014 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
9015
9016 let row = snapshot
9017 .buffer_snapshot
9018 .buffer_line_for_row(MultiBufferRow(point.row))?
9019 .1
9020 .start
9021 .row;
9022
9023 let context_range =
9024 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
9025 Some((
9026 (runnable.buffer_id, row),
9027 RunnableTasks {
9028 templates: tasks,
9029 offset: MultiBufferOffset(runnable.run_range.start),
9030 context_range,
9031 column: point.column,
9032 extra_variables: runnable.extra_captures,
9033 },
9034 ))
9035 })
9036 .collect()
9037 }
9038
9039 fn templates_with_tags(
9040 project: &Model<Project>,
9041 runnable: &mut Runnable,
9042 cx: &WindowContext,
9043 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
9044 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
9045 let (worktree_id, file) = project
9046 .buffer_for_id(runnable.buffer, cx)
9047 .and_then(|buffer| buffer.read(cx).file())
9048 .map(|file| (file.worktree_id(cx), file.clone()))
9049 .unzip();
9050
9051 (
9052 project.task_store().read(cx).task_inventory().cloned(),
9053 worktree_id,
9054 file,
9055 )
9056 });
9057
9058 let tags = mem::take(&mut runnable.tags);
9059 let mut tags: Vec<_> = tags
9060 .into_iter()
9061 .flat_map(|tag| {
9062 let tag = tag.0.clone();
9063 inventory
9064 .as_ref()
9065 .into_iter()
9066 .flat_map(|inventory| {
9067 inventory.read(cx).list_tasks(
9068 file.clone(),
9069 Some(runnable.language.clone()),
9070 worktree_id,
9071 cx,
9072 )
9073 })
9074 .filter(move |(_, template)| {
9075 template.tags.iter().any(|source_tag| source_tag == &tag)
9076 })
9077 })
9078 .sorted_by_key(|(kind, _)| kind.to_owned())
9079 .collect();
9080 if let Some((leading_tag_source, _)) = tags.first() {
9081 // Strongest source wins; if we have worktree tag binding, prefer that to
9082 // global and language bindings;
9083 // if we have a global binding, prefer that to language binding.
9084 let first_mismatch = tags
9085 .iter()
9086 .position(|(tag_source, _)| tag_source != leading_tag_source);
9087 if let Some(index) = first_mismatch {
9088 tags.truncate(index);
9089 }
9090 }
9091
9092 tags
9093 }
9094
9095 pub fn move_to_enclosing_bracket(
9096 &mut self,
9097 _: &MoveToEnclosingBracket,
9098 cx: &mut ViewContext<Self>,
9099 ) {
9100 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9101 s.move_offsets_with(|snapshot, selection| {
9102 let Some(enclosing_bracket_ranges) =
9103 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
9104 else {
9105 return;
9106 };
9107
9108 let mut best_length = usize::MAX;
9109 let mut best_inside = false;
9110 let mut best_in_bracket_range = false;
9111 let mut best_destination = None;
9112 for (open, close) in enclosing_bracket_ranges {
9113 let close = close.to_inclusive();
9114 let length = close.end() - open.start;
9115 let inside = selection.start >= open.end && selection.end <= *close.start();
9116 let in_bracket_range = open.to_inclusive().contains(&selection.head())
9117 || close.contains(&selection.head());
9118
9119 // If best is next to a bracket and current isn't, skip
9120 if !in_bracket_range && best_in_bracket_range {
9121 continue;
9122 }
9123
9124 // Prefer smaller lengths unless best is inside and current isn't
9125 if length > best_length && (best_inside || !inside) {
9126 continue;
9127 }
9128
9129 best_length = length;
9130 best_inside = inside;
9131 best_in_bracket_range = in_bracket_range;
9132 best_destination = Some(
9133 if close.contains(&selection.start) && close.contains(&selection.end) {
9134 if inside {
9135 open.end
9136 } else {
9137 open.start
9138 }
9139 } else if inside {
9140 *close.start()
9141 } else {
9142 *close.end()
9143 },
9144 );
9145 }
9146
9147 if let Some(destination) = best_destination {
9148 selection.collapse_to(destination, SelectionGoal::None);
9149 }
9150 })
9151 });
9152 }
9153
9154 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
9155 self.end_selection(cx);
9156 self.selection_history.mode = SelectionHistoryMode::Undoing;
9157 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
9158 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9159 self.select_next_state = entry.select_next_state;
9160 self.select_prev_state = entry.select_prev_state;
9161 self.add_selections_state = entry.add_selections_state;
9162 self.request_autoscroll(Autoscroll::newest(), cx);
9163 }
9164 self.selection_history.mode = SelectionHistoryMode::Normal;
9165 }
9166
9167 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
9168 self.end_selection(cx);
9169 self.selection_history.mode = SelectionHistoryMode::Redoing;
9170 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
9171 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
9172 self.select_next_state = entry.select_next_state;
9173 self.select_prev_state = entry.select_prev_state;
9174 self.add_selections_state = entry.add_selections_state;
9175 self.request_autoscroll(Autoscroll::newest(), cx);
9176 }
9177 self.selection_history.mode = SelectionHistoryMode::Normal;
9178 }
9179
9180 pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
9181 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
9182 }
9183
9184 pub fn expand_excerpts_down(
9185 &mut self,
9186 action: &ExpandExcerptsDown,
9187 cx: &mut ViewContext<Self>,
9188 ) {
9189 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
9190 }
9191
9192 pub fn expand_excerpts_up(&mut self, action: &ExpandExcerptsUp, cx: &mut ViewContext<Self>) {
9193 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
9194 }
9195
9196 pub fn expand_excerpts_for_direction(
9197 &mut self,
9198 lines: u32,
9199 direction: ExpandExcerptDirection,
9200 cx: &mut ViewContext<Self>,
9201 ) {
9202 let selections = self.selections.disjoint_anchors();
9203
9204 let lines = if lines == 0 {
9205 EditorSettings::get_global(cx).expand_excerpt_lines
9206 } else {
9207 lines
9208 };
9209
9210 self.buffer.update(cx, |buffer, cx| {
9211 let snapshot = buffer.snapshot(cx);
9212 let mut excerpt_ids = selections
9213 .iter()
9214 .flat_map(|selection| {
9215 snapshot
9216 .excerpts_for_range(selection.range())
9217 .map(|excerpt| excerpt.id())
9218 })
9219 .collect::<Vec<_>>();
9220 excerpt_ids.sort();
9221 excerpt_ids.dedup();
9222 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
9223 })
9224 }
9225
9226 pub fn expand_excerpt(
9227 &mut self,
9228 excerpt: ExcerptId,
9229 direction: ExpandExcerptDirection,
9230 cx: &mut ViewContext<Self>,
9231 ) {
9232 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
9233 self.buffer.update(cx, |buffer, cx| {
9234 buffer.expand_excerpts([excerpt], lines, direction, cx)
9235 })
9236 }
9237
9238 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
9239 self.go_to_diagnostic_impl(Direction::Next, cx)
9240 }
9241
9242 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
9243 self.go_to_diagnostic_impl(Direction::Prev, cx)
9244 }
9245
9246 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
9247 let buffer = self.buffer.read(cx).snapshot(cx);
9248 let selection = self.selections.newest::<usize>(cx);
9249
9250 // If there is an active Diagnostic Popover jump to its diagnostic instead.
9251 if direction == Direction::Next {
9252 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
9253 self.activate_diagnostics(popover.group_id(), cx);
9254 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
9255 let primary_range_start = active_diagnostics.primary_range.start;
9256 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9257 let mut new_selection = s.newest_anchor().clone();
9258 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
9259 s.select_anchors(vec![new_selection.clone()]);
9260 });
9261 }
9262 return;
9263 }
9264 }
9265
9266 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
9267 active_diagnostics
9268 .primary_range
9269 .to_offset(&buffer)
9270 .to_inclusive()
9271 });
9272 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
9273 if active_primary_range.contains(&selection.head()) {
9274 *active_primary_range.start()
9275 } else {
9276 selection.head()
9277 }
9278 } else {
9279 selection.head()
9280 };
9281 let snapshot = self.snapshot(cx);
9282 loop {
9283 let diagnostics = if direction == Direction::Prev {
9284 buffer
9285 .diagnostics_in_range(0..search_start, true)
9286 .map(|DiagnosticEntry { diagnostic, range }| DiagnosticEntry {
9287 diagnostic,
9288 range: range.to_offset(&buffer),
9289 })
9290 .collect::<Vec<_>>()
9291 } else {
9292 buffer
9293 .diagnostics_in_range(search_start..buffer.len(), false)
9294 .map(|DiagnosticEntry { diagnostic, range }| DiagnosticEntry {
9295 diagnostic,
9296 range: range.to_offset(&buffer),
9297 })
9298 .collect::<Vec<_>>()
9299 }
9300 .into_iter()
9301 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start));
9302 let group = diagnostics
9303 // relies on diagnostics_in_range to return diagnostics with the same starting range to
9304 // be sorted in a stable way
9305 // skip until we are at current active diagnostic, if it exists
9306 .skip_while(|entry| {
9307 (match direction {
9308 Direction::Prev => entry.range.start >= search_start,
9309 Direction::Next => entry.range.start <= search_start,
9310 }) && self
9311 .active_diagnostics
9312 .as_ref()
9313 .is_some_and(|a| a.group_id != entry.diagnostic.group_id)
9314 })
9315 .find_map(|entry| {
9316 if entry.diagnostic.is_primary
9317 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
9318 && !entry.range.is_empty()
9319 // if we match with the active diagnostic, skip it
9320 && Some(entry.diagnostic.group_id)
9321 != self.active_diagnostics.as_ref().map(|d| d.group_id)
9322 {
9323 Some((entry.range, entry.diagnostic.group_id))
9324 } else {
9325 None
9326 }
9327 });
9328
9329 if let Some((primary_range, group_id)) = group {
9330 self.activate_diagnostics(group_id, cx);
9331 if self.active_diagnostics.is_some() {
9332 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9333 s.select(vec![Selection {
9334 id: selection.id,
9335 start: primary_range.start,
9336 end: primary_range.start,
9337 reversed: false,
9338 goal: SelectionGoal::None,
9339 }]);
9340 });
9341 }
9342 break;
9343 } else {
9344 // Cycle around to the start of the buffer, potentially moving back to the start of
9345 // the currently active diagnostic.
9346 active_primary_range.take();
9347 if direction == Direction::Prev {
9348 if search_start == buffer.len() {
9349 break;
9350 } else {
9351 search_start = buffer.len();
9352 }
9353 } else if search_start == 0 {
9354 break;
9355 } else {
9356 search_start = 0;
9357 }
9358 }
9359 }
9360 }
9361
9362 fn go_to_next_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
9363 let snapshot = self.snapshot(cx);
9364 let selection = self.selections.newest::<Point>(cx);
9365 self.go_to_hunk_after_position(&snapshot, selection.head(), cx);
9366 }
9367
9368 fn go_to_hunk_after_position(
9369 &mut self,
9370 snapshot: &EditorSnapshot,
9371 position: Point,
9372 cx: &mut ViewContext<Editor>,
9373 ) -> Option<MultiBufferDiffHunk> {
9374 for (ix, position) in [position, Point::zero()].into_iter().enumerate() {
9375 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9376 snapshot,
9377 position,
9378 ix > 0,
9379 snapshot.diff_map.diff_hunks_in_range(
9380 position + Point::new(1, 0)..snapshot.buffer_snapshot.max_point(),
9381 &snapshot.buffer_snapshot,
9382 ),
9383 cx,
9384 ) {
9385 return Some(hunk);
9386 }
9387 }
9388 None
9389 }
9390
9391 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
9392 let snapshot = self.snapshot(cx);
9393 let selection = self.selections.newest::<Point>(cx);
9394 self.go_to_hunk_before_position(&snapshot, selection.head(), cx);
9395 }
9396
9397 fn go_to_hunk_before_position(
9398 &mut self,
9399 snapshot: &EditorSnapshot,
9400 position: Point,
9401 cx: &mut ViewContext<Editor>,
9402 ) -> Option<MultiBufferDiffHunk> {
9403 for (ix, position) in [position, snapshot.buffer_snapshot.max_point()]
9404 .into_iter()
9405 .enumerate()
9406 {
9407 if let Some(hunk) = self.go_to_next_hunk_in_direction(
9408 snapshot,
9409 position,
9410 ix > 0,
9411 snapshot
9412 .diff_map
9413 .diff_hunks_in_range_rev(Point::zero()..position, &snapshot.buffer_snapshot),
9414 cx,
9415 ) {
9416 return Some(hunk);
9417 }
9418 }
9419 None
9420 }
9421
9422 fn go_to_next_hunk_in_direction(
9423 &mut self,
9424 snapshot: &DisplaySnapshot,
9425 initial_point: Point,
9426 is_wrapped: bool,
9427 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
9428 cx: &mut ViewContext<Editor>,
9429 ) -> Option<MultiBufferDiffHunk> {
9430 let display_point = initial_point.to_display_point(snapshot);
9431 let mut hunks = hunks
9432 .map(|hunk| (diff_hunk_to_display(&hunk, snapshot), hunk))
9433 .filter(|(display_hunk, _)| {
9434 is_wrapped || !display_hunk.contains_display_row(display_point.row())
9435 })
9436 .dedup();
9437
9438 if let Some((display_hunk, hunk)) = hunks.next() {
9439 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
9440 let row = display_hunk.start_display_row();
9441 let point = DisplayPoint::new(row, 0);
9442 s.select_display_ranges([point..point]);
9443 });
9444
9445 Some(hunk)
9446 } else {
9447 None
9448 }
9449 }
9450
9451 pub fn go_to_definition(
9452 &mut self,
9453 _: &GoToDefinition,
9454 cx: &mut ViewContext<Self>,
9455 ) -> Task<Result<Navigated>> {
9456 let definition = self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
9457 cx.spawn(|editor, mut cx| async move {
9458 if definition.await? == Navigated::Yes {
9459 return Ok(Navigated::Yes);
9460 }
9461 match editor.update(&mut cx, |editor, cx| {
9462 editor.find_all_references(&FindAllReferences, cx)
9463 })? {
9464 Some(references) => references.await,
9465 None => Ok(Navigated::No),
9466 }
9467 })
9468 }
9469
9470 pub fn go_to_declaration(
9471 &mut self,
9472 _: &GoToDeclaration,
9473 cx: &mut ViewContext<Self>,
9474 ) -> Task<Result<Navigated>> {
9475 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, cx)
9476 }
9477
9478 pub fn go_to_declaration_split(
9479 &mut self,
9480 _: &GoToDeclaration,
9481 cx: &mut ViewContext<Self>,
9482 ) -> Task<Result<Navigated>> {
9483 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, cx)
9484 }
9485
9486 pub fn go_to_implementation(
9487 &mut self,
9488 _: &GoToImplementation,
9489 cx: &mut ViewContext<Self>,
9490 ) -> Task<Result<Navigated>> {
9491 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, cx)
9492 }
9493
9494 pub fn go_to_implementation_split(
9495 &mut self,
9496 _: &GoToImplementationSplit,
9497 cx: &mut ViewContext<Self>,
9498 ) -> Task<Result<Navigated>> {
9499 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, cx)
9500 }
9501
9502 pub fn go_to_type_definition(
9503 &mut self,
9504 _: &GoToTypeDefinition,
9505 cx: &mut ViewContext<Self>,
9506 ) -> Task<Result<Navigated>> {
9507 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx)
9508 }
9509
9510 pub fn go_to_definition_split(
9511 &mut self,
9512 _: &GoToDefinitionSplit,
9513 cx: &mut ViewContext<Self>,
9514 ) -> Task<Result<Navigated>> {
9515 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx)
9516 }
9517
9518 pub fn go_to_type_definition_split(
9519 &mut self,
9520 _: &GoToTypeDefinitionSplit,
9521 cx: &mut ViewContext<Self>,
9522 ) -> Task<Result<Navigated>> {
9523 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx)
9524 }
9525
9526 fn go_to_definition_of_kind(
9527 &mut self,
9528 kind: GotoDefinitionKind,
9529 split: bool,
9530 cx: &mut ViewContext<Self>,
9531 ) -> Task<Result<Navigated>> {
9532 let Some(provider) = self.semantics_provider.clone() else {
9533 return Task::ready(Ok(Navigated::No));
9534 };
9535 let head = self.selections.newest::<usize>(cx).head();
9536 let buffer = self.buffer.read(cx);
9537 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
9538 text_anchor
9539 } else {
9540 return Task::ready(Ok(Navigated::No));
9541 };
9542
9543 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
9544 return Task::ready(Ok(Navigated::No));
9545 };
9546
9547 cx.spawn(|editor, mut cx| async move {
9548 let definitions = definitions.await?;
9549 let navigated = editor
9550 .update(&mut cx, |editor, cx| {
9551 editor.navigate_to_hover_links(
9552 Some(kind),
9553 definitions
9554 .into_iter()
9555 .filter(|location| {
9556 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
9557 })
9558 .map(HoverLink::Text)
9559 .collect::<Vec<_>>(),
9560 split,
9561 cx,
9562 )
9563 })?
9564 .await?;
9565 anyhow::Ok(navigated)
9566 })
9567 }
9568
9569 pub fn open_url(&mut self, _: &OpenUrl, cx: &mut ViewContext<Self>) {
9570 let selection = self.selections.newest_anchor();
9571 let head = selection.head();
9572 let tail = selection.tail();
9573
9574 let Some((buffer, start_position)) =
9575 self.buffer.read(cx).text_anchor_for_position(head, cx)
9576 else {
9577 return;
9578 };
9579
9580 let end_position = if head != tail {
9581 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
9582 return;
9583 };
9584 Some(pos)
9585 } else {
9586 None
9587 };
9588
9589 let url_finder = cx.spawn(|editor, mut cx| async move {
9590 let url = if let Some(end_pos) = end_position {
9591 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
9592 } else {
9593 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
9594 };
9595
9596 if let Some(url) = url {
9597 editor.update(&mut cx, |_, cx| {
9598 cx.open_url(&url);
9599 })
9600 } else {
9601 Ok(())
9602 }
9603 });
9604
9605 url_finder.detach();
9606 }
9607
9608 pub fn open_selected_filename(&mut self, _: &OpenSelectedFilename, cx: &mut ViewContext<Self>) {
9609 let Some(workspace) = self.workspace() else {
9610 return;
9611 };
9612
9613 let position = self.selections.newest_anchor().head();
9614
9615 let Some((buffer, buffer_position)) =
9616 self.buffer.read(cx).text_anchor_for_position(position, cx)
9617 else {
9618 return;
9619 };
9620
9621 let project = self.project.clone();
9622
9623 cx.spawn(|_, mut cx| async move {
9624 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
9625
9626 if let Some((_, path)) = result {
9627 workspace
9628 .update(&mut cx, |workspace, cx| {
9629 workspace.open_resolved_path(path, cx)
9630 })?
9631 .await?;
9632 }
9633 anyhow::Ok(())
9634 })
9635 .detach();
9636 }
9637
9638 pub(crate) fn navigate_to_hover_links(
9639 &mut self,
9640 kind: Option<GotoDefinitionKind>,
9641 mut definitions: Vec<HoverLink>,
9642 split: bool,
9643 cx: &mut ViewContext<Editor>,
9644 ) -> Task<Result<Navigated>> {
9645 // If there is one definition, just open it directly
9646 if definitions.len() == 1 {
9647 let definition = definitions.pop().unwrap();
9648
9649 enum TargetTaskResult {
9650 Location(Option<Location>),
9651 AlreadyNavigated,
9652 }
9653
9654 let target_task = match definition {
9655 HoverLink::Text(link) => {
9656 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
9657 }
9658 HoverLink::InlayHint(lsp_location, server_id) => {
9659 let computation = self.compute_target_location(lsp_location, server_id, cx);
9660 cx.background_executor().spawn(async move {
9661 let location = computation.await?;
9662 Ok(TargetTaskResult::Location(location))
9663 })
9664 }
9665 HoverLink::Url(url) => {
9666 cx.open_url(&url);
9667 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
9668 }
9669 HoverLink::File(path) => {
9670 if let Some(workspace) = self.workspace() {
9671 cx.spawn(|_, mut cx| async move {
9672 workspace
9673 .update(&mut cx, |workspace, cx| {
9674 workspace.open_resolved_path(path, cx)
9675 })?
9676 .await
9677 .map(|_| TargetTaskResult::AlreadyNavigated)
9678 })
9679 } else {
9680 Task::ready(Ok(TargetTaskResult::Location(None)))
9681 }
9682 }
9683 };
9684 cx.spawn(|editor, mut cx| async move {
9685 let target = match target_task.await.context("target resolution task")? {
9686 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
9687 TargetTaskResult::Location(None) => return Ok(Navigated::No),
9688 TargetTaskResult::Location(Some(target)) => target,
9689 };
9690
9691 editor.update(&mut cx, |editor, cx| {
9692 let Some(workspace) = editor.workspace() else {
9693 return Navigated::No;
9694 };
9695 let pane = workspace.read(cx).active_pane().clone();
9696
9697 let range = target.range.to_offset(target.buffer.read(cx));
9698 let range = editor.range_for_match(&range);
9699
9700 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
9701 let buffer = target.buffer.read(cx);
9702 let range = check_multiline_range(buffer, range);
9703 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
9704 s.select_ranges([range]);
9705 });
9706 } else {
9707 cx.window_context().defer(move |cx| {
9708 let target_editor: View<Self> =
9709 workspace.update(cx, |workspace, cx| {
9710 let pane = if split {
9711 workspace.adjacent_pane(cx)
9712 } else {
9713 workspace.active_pane().clone()
9714 };
9715
9716 workspace.open_project_item(
9717 pane,
9718 target.buffer.clone(),
9719 true,
9720 true,
9721 cx,
9722 )
9723 });
9724 target_editor.update(cx, |target_editor, cx| {
9725 // When selecting a definition in a different buffer, disable the nav history
9726 // to avoid creating a history entry at the previous cursor location.
9727 pane.update(cx, |pane, _| pane.disable_history());
9728 let buffer = target.buffer.read(cx);
9729 let range = check_multiline_range(buffer, range);
9730 target_editor.change_selections(
9731 Some(Autoscroll::focused()),
9732 cx,
9733 |s| {
9734 s.select_ranges([range]);
9735 },
9736 );
9737 pane.update(cx, |pane, _| pane.enable_history());
9738 });
9739 });
9740 }
9741 Navigated::Yes
9742 })
9743 })
9744 } else if !definitions.is_empty() {
9745 cx.spawn(|editor, mut cx| async move {
9746 let (title, location_tasks, workspace) = editor
9747 .update(&mut cx, |editor, cx| {
9748 let tab_kind = match kind {
9749 Some(GotoDefinitionKind::Implementation) => "Implementations",
9750 _ => "Definitions",
9751 };
9752 let title = definitions
9753 .iter()
9754 .find_map(|definition| match definition {
9755 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
9756 let buffer = origin.buffer.read(cx);
9757 format!(
9758 "{} for {}",
9759 tab_kind,
9760 buffer
9761 .text_for_range(origin.range.clone())
9762 .collect::<String>()
9763 )
9764 }),
9765 HoverLink::InlayHint(_, _) => None,
9766 HoverLink::Url(_) => None,
9767 HoverLink::File(_) => None,
9768 })
9769 .unwrap_or(tab_kind.to_string());
9770 let location_tasks = definitions
9771 .into_iter()
9772 .map(|definition| match definition {
9773 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
9774 HoverLink::InlayHint(lsp_location, server_id) => {
9775 editor.compute_target_location(lsp_location, server_id, cx)
9776 }
9777 HoverLink::Url(_) => Task::ready(Ok(None)),
9778 HoverLink::File(_) => Task::ready(Ok(None)),
9779 })
9780 .collect::<Vec<_>>();
9781 (title, location_tasks, editor.workspace().clone())
9782 })
9783 .context("location tasks preparation")?;
9784
9785 let locations = future::join_all(location_tasks)
9786 .await
9787 .into_iter()
9788 .filter_map(|location| location.transpose())
9789 .collect::<Result<_>>()
9790 .context("location tasks")?;
9791
9792 let Some(workspace) = workspace else {
9793 return Ok(Navigated::No);
9794 };
9795 let opened = workspace
9796 .update(&mut cx, |workspace, cx| {
9797 Self::open_locations_in_multibuffer(workspace, locations, title, split, cx)
9798 })
9799 .ok();
9800
9801 anyhow::Ok(Navigated::from_bool(opened.is_some()))
9802 })
9803 } else {
9804 Task::ready(Ok(Navigated::No))
9805 }
9806 }
9807
9808 fn compute_target_location(
9809 &self,
9810 lsp_location: lsp::Location,
9811 server_id: LanguageServerId,
9812 cx: &mut ViewContext<Self>,
9813 ) -> Task<anyhow::Result<Option<Location>>> {
9814 let Some(project) = self.project.clone() else {
9815 return Task::ready(Ok(None));
9816 };
9817
9818 cx.spawn(move |editor, mut cx| async move {
9819 let location_task = editor.update(&mut cx, |_, cx| {
9820 project.update(cx, |project, cx| {
9821 let language_server_name = project
9822 .language_server_statuses(cx)
9823 .find(|(id, _)| server_id == *id)
9824 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
9825 language_server_name.map(|language_server_name| {
9826 project.open_local_buffer_via_lsp(
9827 lsp_location.uri.clone(),
9828 server_id,
9829 language_server_name,
9830 cx,
9831 )
9832 })
9833 })
9834 })?;
9835 let location = match location_task {
9836 Some(task) => Some({
9837 let target_buffer_handle = task.await.context("open local buffer")?;
9838 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
9839 let target_start = target_buffer
9840 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
9841 let target_end = target_buffer
9842 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
9843 target_buffer.anchor_after(target_start)
9844 ..target_buffer.anchor_before(target_end)
9845 })?;
9846 Location {
9847 buffer: target_buffer_handle,
9848 range,
9849 }
9850 }),
9851 None => None,
9852 };
9853 Ok(location)
9854 })
9855 }
9856
9857 pub fn find_all_references(
9858 &mut self,
9859 _: &FindAllReferences,
9860 cx: &mut ViewContext<Self>,
9861 ) -> Option<Task<Result<Navigated>>> {
9862 let selection = self.selections.newest::<usize>(cx);
9863 let multi_buffer = self.buffer.read(cx);
9864 let head = selection.head();
9865
9866 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
9867 let head_anchor = multi_buffer_snapshot.anchor_at(
9868 head,
9869 if head < selection.tail() {
9870 Bias::Right
9871 } else {
9872 Bias::Left
9873 },
9874 );
9875
9876 match self
9877 .find_all_references_task_sources
9878 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
9879 {
9880 Ok(_) => {
9881 log::info!(
9882 "Ignoring repeated FindAllReferences invocation with the position of already running task"
9883 );
9884 return None;
9885 }
9886 Err(i) => {
9887 self.find_all_references_task_sources.insert(i, head_anchor);
9888 }
9889 }
9890
9891 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
9892 let workspace = self.workspace()?;
9893 let project = workspace.read(cx).project().clone();
9894 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
9895 Some(cx.spawn(|editor, mut cx| async move {
9896 let _cleanup = defer({
9897 let mut cx = cx.clone();
9898 move || {
9899 let _ = editor.update(&mut cx, |editor, _| {
9900 if let Ok(i) =
9901 editor
9902 .find_all_references_task_sources
9903 .binary_search_by(|anchor| {
9904 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
9905 })
9906 {
9907 editor.find_all_references_task_sources.remove(i);
9908 }
9909 });
9910 }
9911 });
9912
9913 let locations = references.await?;
9914 if locations.is_empty() {
9915 return anyhow::Ok(Navigated::No);
9916 }
9917
9918 workspace.update(&mut cx, |workspace, cx| {
9919 let title = locations
9920 .first()
9921 .as_ref()
9922 .map(|location| {
9923 let buffer = location.buffer.read(cx);
9924 format!(
9925 "References to `{}`",
9926 buffer
9927 .text_for_range(location.range.clone())
9928 .collect::<String>()
9929 )
9930 })
9931 .unwrap();
9932 Self::open_locations_in_multibuffer(workspace, locations, title, false, cx);
9933 Navigated::Yes
9934 })
9935 }))
9936 }
9937
9938 /// Opens a multibuffer with the given project locations in it
9939 pub fn open_locations_in_multibuffer(
9940 workspace: &mut Workspace,
9941 mut locations: Vec<Location>,
9942 title: String,
9943 split: bool,
9944 cx: &mut ViewContext<Workspace>,
9945 ) {
9946 // If there are multiple definitions, open them in a multibuffer
9947 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
9948 let mut locations = locations.into_iter().peekable();
9949 let mut ranges_to_highlight = Vec::new();
9950 let capability = workspace.project().read(cx).capability();
9951
9952 let excerpt_buffer = cx.new_model(|cx| {
9953 let mut multibuffer = MultiBuffer::new(capability);
9954 while let Some(location) = locations.next() {
9955 let buffer = location.buffer.read(cx);
9956 let mut ranges_for_buffer = Vec::new();
9957 let range = location.range.to_offset(buffer);
9958 ranges_for_buffer.push(range.clone());
9959
9960 while let Some(next_location) = locations.peek() {
9961 if next_location.buffer == location.buffer {
9962 ranges_for_buffer.push(next_location.range.to_offset(buffer));
9963 locations.next();
9964 } else {
9965 break;
9966 }
9967 }
9968
9969 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
9970 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
9971 location.buffer.clone(),
9972 ranges_for_buffer,
9973 DEFAULT_MULTIBUFFER_CONTEXT,
9974 cx,
9975 ))
9976 }
9977
9978 multibuffer.with_title(title)
9979 });
9980
9981 let editor = cx.new_view(|cx| {
9982 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx)
9983 });
9984 editor.update(cx, |editor, cx| {
9985 if let Some(first_range) = ranges_to_highlight.first() {
9986 editor.change_selections(None, cx, |selections| {
9987 selections.clear_disjoint();
9988 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
9989 });
9990 }
9991 editor.highlight_background::<Self>(
9992 &ranges_to_highlight,
9993 |theme| theme.editor_highlighted_line_background,
9994 cx,
9995 );
9996 editor.register_buffers_with_language_servers(cx);
9997 });
9998
9999 let item = Box::new(editor);
10000 let item_id = item.item_id();
10001
10002 if split {
10003 workspace.split_item(SplitDirection::Right, item.clone(), cx);
10004 } else {
10005 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
10006 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
10007 pane.close_current_preview_item(cx)
10008 } else {
10009 None
10010 }
10011 });
10012 workspace.add_item_to_active_pane(item.clone(), destination_index, true, cx);
10013 }
10014 workspace.active_pane().update(cx, |pane, cx| {
10015 pane.set_preview_item_id(Some(item_id), cx);
10016 });
10017 }
10018
10019 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10020 use language::ToOffset as _;
10021
10022 let provider = self.semantics_provider.clone()?;
10023 let selection = self.selections.newest_anchor().clone();
10024 let (cursor_buffer, cursor_buffer_position) = self
10025 .buffer
10026 .read(cx)
10027 .text_anchor_for_position(selection.head(), cx)?;
10028 let (tail_buffer, cursor_buffer_position_end) = self
10029 .buffer
10030 .read(cx)
10031 .text_anchor_for_position(selection.tail(), cx)?;
10032 if tail_buffer != cursor_buffer {
10033 return None;
10034 }
10035
10036 let snapshot = cursor_buffer.read(cx).snapshot();
10037 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
10038 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
10039 let prepare_rename = provider
10040 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
10041 .unwrap_or_else(|| Task::ready(Ok(None)));
10042 drop(snapshot);
10043
10044 Some(cx.spawn(|this, mut cx| async move {
10045 let rename_range = if let Some(range) = prepare_rename.await? {
10046 Some(range)
10047 } else {
10048 this.update(&mut cx, |this, cx| {
10049 let buffer = this.buffer.read(cx).snapshot(cx);
10050 let mut buffer_highlights = this
10051 .document_highlights_for_position(selection.head(), &buffer)
10052 .filter(|highlight| {
10053 highlight.start.excerpt_id == selection.head().excerpt_id
10054 && highlight.end.excerpt_id == selection.head().excerpt_id
10055 });
10056 buffer_highlights
10057 .next()
10058 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
10059 })?
10060 };
10061 if let Some(rename_range) = rename_range {
10062 this.update(&mut cx, |this, cx| {
10063 let snapshot = cursor_buffer.read(cx).snapshot();
10064 let rename_buffer_range = rename_range.to_offset(&snapshot);
10065 let cursor_offset_in_rename_range =
10066 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
10067 let cursor_offset_in_rename_range_end =
10068 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
10069
10070 this.take_rename(false, cx);
10071 let buffer = this.buffer.read(cx).read(cx);
10072 let cursor_offset = selection.head().to_offset(&buffer);
10073 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
10074 let rename_end = rename_start + rename_buffer_range.len();
10075 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
10076 let mut old_highlight_id = None;
10077 let old_name: Arc<str> = buffer
10078 .chunks(rename_start..rename_end, true)
10079 .map(|chunk| {
10080 if old_highlight_id.is_none() {
10081 old_highlight_id = chunk.syntax_highlight_id;
10082 }
10083 chunk.text
10084 })
10085 .collect::<String>()
10086 .into();
10087
10088 drop(buffer);
10089
10090 // Position the selection in the rename editor so that it matches the current selection.
10091 this.show_local_selections = false;
10092 let rename_editor = cx.new_view(|cx| {
10093 let mut editor = Editor::single_line(cx);
10094 editor.buffer.update(cx, |buffer, cx| {
10095 buffer.edit([(0..0, old_name.clone())], None, cx)
10096 });
10097 let rename_selection_range = match cursor_offset_in_rename_range
10098 .cmp(&cursor_offset_in_rename_range_end)
10099 {
10100 Ordering::Equal => {
10101 editor.select_all(&SelectAll, cx);
10102 return editor;
10103 }
10104 Ordering::Less => {
10105 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
10106 }
10107 Ordering::Greater => {
10108 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
10109 }
10110 };
10111 if rename_selection_range.end > old_name.len() {
10112 editor.select_all(&SelectAll, cx);
10113 } else {
10114 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
10115 s.select_ranges([rename_selection_range]);
10116 });
10117 }
10118 editor
10119 });
10120 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
10121 if e == &EditorEvent::Focused {
10122 cx.emit(EditorEvent::FocusedIn)
10123 }
10124 })
10125 .detach();
10126
10127 let write_highlights =
10128 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
10129 let read_highlights =
10130 this.clear_background_highlights::<DocumentHighlightRead>(cx);
10131 let ranges = write_highlights
10132 .iter()
10133 .flat_map(|(_, ranges)| ranges.iter())
10134 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
10135 .cloned()
10136 .collect();
10137
10138 this.highlight_text::<Rename>(
10139 ranges,
10140 HighlightStyle {
10141 fade_out: Some(0.6),
10142 ..Default::default()
10143 },
10144 cx,
10145 );
10146 let rename_focus_handle = rename_editor.focus_handle(cx);
10147 cx.focus(&rename_focus_handle);
10148 let block_id = this.insert_blocks(
10149 [BlockProperties {
10150 style: BlockStyle::Flex,
10151 placement: BlockPlacement::Below(range.start),
10152 height: 1,
10153 render: Arc::new({
10154 let rename_editor = rename_editor.clone();
10155 move |cx: &mut BlockContext| {
10156 let mut text_style = cx.editor_style.text.clone();
10157 if let Some(highlight_style) = old_highlight_id
10158 .and_then(|h| h.style(&cx.editor_style.syntax))
10159 {
10160 text_style = text_style.highlight(highlight_style);
10161 }
10162 div()
10163 .block_mouse_down()
10164 .pl(cx.anchor_x)
10165 .child(EditorElement::new(
10166 &rename_editor,
10167 EditorStyle {
10168 background: cx.theme().system().transparent,
10169 local_player: cx.editor_style.local_player,
10170 text: text_style,
10171 scrollbar_width: cx.editor_style.scrollbar_width,
10172 syntax: cx.editor_style.syntax.clone(),
10173 status: cx.editor_style.status.clone(),
10174 inlay_hints_style: HighlightStyle {
10175 font_weight: Some(FontWeight::BOLD),
10176 ..make_inlay_hints_style(cx)
10177 },
10178 inline_completion_styles: make_suggestion_styles(
10179 cx,
10180 ),
10181 ..EditorStyle::default()
10182 },
10183 ))
10184 .into_any_element()
10185 }
10186 }),
10187 priority: 0,
10188 }],
10189 Some(Autoscroll::fit()),
10190 cx,
10191 )[0];
10192 this.pending_rename = Some(RenameState {
10193 range,
10194 old_name,
10195 editor: rename_editor,
10196 block_id,
10197 });
10198 })?;
10199 }
10200
10201 Ok(())
10202 }))
10203 }
10204
10205 pub fn confirm_rename(
10206 &mut self,
10207 _: &ConfirmRename,
10208 cx: &mut ViewContext<Self>,
10209 ) -> Option<Task<Result<()>>> {
10210 let rename = self.take_rename(false, cx)?;
10211 let workspace = self.workspace()?.downgrade();
10212 let (buffer, start) = self
10213 .buffer
10214 .read(cx)
10215 .text_anchor_for_position(rename.range.start, cx)?;
10216 let (end_buffer, _) = self
10217 .buffer
10218 .read(cx)
10219 .text_anchor_for_position(rename.range.end, cx)?;
10220 if buffer != end_buffer {
10221 return None;
10222 }
10223
10224 let old_name = rename.old_name;
10225 let new_name = rename.editor.read(cx).text(cx);
10226
10227 let rename = self.semantics_provider.as_ref()?.perform_rename(
10228 &buffer,
10229 start,
10230 new_name.clone(),
10231 cx,
10232 )?;
10233
10234 Some(cx.spawn(|editor, mut cx| async move {
10235 let project_transaction = rename.await?;
10236 Self::open_project_transaction(
10237 &editor,
10238 workspace,
10239 project_transaction,
10240 format!("Rename: {} → {}", old_name, new_name),
10241 cx.clone(),
10242 )
10243 .await?;
10244
10245 editor.update(&mut cx, |editor, cx| {
10246 editor.refresh_document_highlights(cx);
10247 })?;
10248 Ok(())
10249 }))
10250 }
10251
10252 fn take_rename(
10253 &mut self,
10254 moving_cursor: bool,
10255 cx: &mut ViewContext<Self>,
10256 ) -> Option<RenameState> {
10257 let rename = self.pending_rename.take()?;
10258 if rename.editor.focus_handle(cx).is_focused(cx) {
10259 cx.focus(&self.focus_handle);
10260 }
10261
10262 self.remove_blocks(
10263 [rename.block_id].into_iter().collect(),
10264 Some(Autoscroll::fit()),
10265 cx,
10266 );
10267 self.clear_highlights::<Rename>(cx);
10268 self.show_local_selections = true;
10269
10270 if moving_cursor {
10271 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
10272 editor.selections.newest::<usize>(cx).head()
10273 });
10274
10275 // Update the selection to match the position of the selection inside
10276 // the rename editor.
10277 let snapshot = self.buffer.read(cx).read(cx);
10278 let rename_range = rename.range.to_offset(&snapshot);
10279 let cursor_in_editor = snapshot
10280 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
10281 .min(rename_range.end);
10282 drop(snapshot);
10283
10284 self.change_selections(None, cx, |s| {
10285 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
10286 });
10287 } else {
10288 self.refresh_document_highlights(cx);
10289 }
10290
10291 Some(rename)
10292 }
10293
10294 pub fn pending_rename(&self) -> Option<&RenameState> {
10295 self.pending_rename.as_ref()
10296 }
10297
10298 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
10299 let project = match &self.project {
10300 Some(project) => project.clone(),
10301 None => return None,
10302 };
10303
10304 Some(self.perform_format(project, FormatTrigger::Manual, FormatTarget::Buffer, cx))
10305 }
10306
10307 fn format_selections(
10308 &mut self,
10309 _: &FormatSelections,
10310 cx: &mut ViewContext<Self>,
10311 ) -> Option<Task<Result<()>>> {
10312 let project = match &self.project {
10313 Some(project) => project.clone(),
10314 None => return None,
10315 };
10316
10317 let selections = self
10318 .selections
10319 .all_adjusted(cx)
10320 .into_iter()
10321 .filter(|s| !s.is_empty())
10322 .collect_vec();
10323
10324 Some(self.perform_format(
10325 project,
10326 FormatTrigger::Manual,
10327 FormatTarget::Ranges(selections),
10328 cx,
10329 ))
10330 }
10331
10332 fn perform_format(
10333 &mut self,
10334 project: Model<Project>,
10335 trigger: FormatTrigger,
10336 target: FormatTarget,
10337 cx: &mut ViewContext<Self>,
10338 ) -> Task<Result<()>> {
10339 let buffer = self.buffer().clone();
10340 let mut buffers = buffer.read(cx).all_buffers();
10341 if trigger == FormatTrigger::Save {
10342 buffers.retain(|buffer| buffer.read(cx).is_dirty());
10343 }
10344
10345 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
10346 let format = project.update(cx, |project, cx| {
10347 project.format(buffers, true, trigger, target, cx)
10348 });
10349
10350 cx.spawn(|_, mut cx| async move {
10351 let transaction = futures::select_biased! {
10352 () = timeout => {
10353 log::warn!("timed out waiting for formatting");
10354 None
10355 }
10356 transaction = format.log_err().fuse() => transaction,
10357 };
10358
10359 buffer
10360 .update(&mut cx, |buffer, cx| {
10361 if let Some(transaction) = transaction {
10362 if !buffer.is_singleton() {
10363 buffer.push_transaction(&transaction.0, cx);
10364 }
10365 }
10366
10367 cx.notify();
10368 })
10369 .ok();
10370
10371 Ok(())
10372 })
10373 }
10374
10375 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
10376 if let Some(project) = self.project.clone() {
10377 self.buffer.update(cx, |multi_buffer, cx| {
10378 project.update(cx, |project, cx| {
10379 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
10380 });
10381 })
10382 }
10383 }
10384
10385 fn cancel_language_server_work(
10386 &mut self,
10387 _: &actions::CancelLanguageServerWork,
10388 cx: &mut ViewContext<Self>,
10389 ) {
10390 if let Some(project) = self.project.clone() {
10391 self.buffer.update(cx, |multi_buffer, cx| {
10392 project.update(cx, |project, cx| {
10393 project.cancel_language_server_work_for_buffers(multi_buffer.all_buffers(), cx);
10394 });
10395 })
10396 }
10397 }
10398
10399 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
10400 cx.show_character_palette();
10401 }
10402
10403 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
10404 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
10405 let buffer = self.buffer.read(cx).snapshot(cx);
10406 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
10407 let is_valid = buffer
10408 .diagnostics_in_range(active_diagnostics.primary_range.clone(), false)
10409 .any(|entry| {
10410 let range = entry.range.to_offset(&buffer);
10411 entry.diagnostic.is_primary
10412 && !range.is_empty()
10413 && range.start == primary_range_start
10414 && entry.diagnostic.message == active_diagnostics.primary_message
10415 });
10416
10417 if is_valid != active_diagnostics.is_valid {
10418 active_diagnostics.is_valid = is_valid;
10419 let mut new_styles = HashMap::default();
10420 for (block_id, diagnostic) in &active_diagnostics.blocks {
10421 new_styles.insert(
10422 *block_id,
10423 diagnostic_block_renderer(diagnostic.clone(), None, true, is_valid),
10424 );
10425 }
10426 self.display_map.update(cx, |display_map, _cx| {
10427 display_map.replace_blocks(new_styles)
10428 });
10429 }
10430 }
10431 }
10432
10433 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
10434 self.dismiss_diagnostics(cx);
10435 let snapshot = self.snapshot(cx);
10436 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
10437 let buffer = self.buffer.read(cx).snapshot(cx);
10438
10439 let mut primary_range = None;
10440 let mut primary_message = None;
10441 let mut group_end = Point::zero();
10442 let diagnostic_group = buffer
10443 .diagnostic_group(group_id)
10444 .filter_map(|entry| {
10445 let start = entry.range.start.to_point(&buffer);
10446 let end = entry.range.end.to_point(&buffer);
10447 if snapshot.is_line_folded(MultiBufferRow(start.row))
10448 && (start.row == end.row
10449 || snapshot.is_line_folded(MultiBufferRow(end.row)))
10450 {
10451 return None;
10452 }
10453 if end > group_end {
10454 group_end = end;
10455 }
10456 if entry.diagnostic.is_primary {
10457 primary_range = Some(entry.range.clone());
10458 primary_message = Some(entry.diagnostic.message.clone());
10459 }
10460 Some(entry)
10461 })
10462 .collect::<Vec<_>>();
10463 let primary_range = primary_range?;
10464 let primary_message = primary_message?;
10465
10466 let blocks = display_map
10467 .insert_blocks(
10468 diagnostic_group.iter().map(|entry| {
10469 let diagnostic = entry.diagnostic.clone();
10470 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
10471 BlockProperties {
10472 style: BlockStyle::Fixed,
10473 placement: BlockPlacement::Below(
10474 buffer.anchor_after(entry.range.start),
10475 ),
10476 height: message_height,
10477 render: diagnostic_block_renderer(diagnostic, None, true, true),
10478 priority: 0,
10479 }
10480 }),
10481 cx,
10482 )
10483 .into_iter()
10484 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
10485 .collect();
10486
10487 Some(ActiveDiagnosticGroup {
10488 primary_range,
10489 primary_message,
10490 group_id,
10491 blocks,
10492 is_valid: true,
10493 })
10494 });
10495 }
10496
10497 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
10498 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
10499 self.display_map.update(cx, |display_map, cx| {
10500 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
10501 });
10502 cx.notify();
10503 }
10504 }
10505
10506 pub fn set_selections_from_remote(
10507 &mut self,
10508 selections: Vec<Selection<Anchor>>,
10509 pending_selection: Option<Selection<Anchor>>,
10510 cx: &mut ViewContext<Self>,
10511 ) {
10512 let old_cursor_position = self.selections.newest_anchor().head();
10513 self.selections.change_with(cx, |s| {
10514 s.select_anchors(selections);
10515 if let Some(pending_selection) = pending_selection {
10516 s.set_pending(pending_selection, SelectMode::Character);
10517 } else {
10518 s.clear_pending();
10519 }
10520 });
10521 self.selections_did_change(false, &old_cursor_position, true, cx);
10522 }
10523
10524 fn push_to_selection_history(&mut self) {
10525 self.selection_history.push(SelectionHistoryEntry {
10526 selections: self.selections.disjoint_anchors(),
10527 select_next_state: self.select_next_state.clone(),
10528 select_prev_state: self.select_prev_state.clone(),
10529 add_selections_state: self.add_selections_state.clone(),
10530 });
10531 }
10532
10533 pub fn transact(
10534 &mut self,
10535 cx: &mut ViewContext<Self>,
10536 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
10537 ) -> Option<TransactionId> {
10538 self.start_transaction_at(Instant::now(), cx);
10539 update(self, cx);
10540 self.end_transaction_at(Instant::now(), cx)
10541 }
10542
10543 pub fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
10544 self.end_selection(cx);
10545 if let Some(tx_id) = self
10546 .buffer
10547 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
10548 {
10549 self.selection_history
10550 .insert_transaction(tx_id, self.selections.disjoint_anchors());
10551 cx.emit(EditorEvent::TransactionBegun {
10552 transaction_id: tx_id,
10553 })
10554 }
10555 }
10556
10557 pub fn end_transaction_at(
10558 &mut self,
10559 now: Instant,
10560 cx: &mut ViewContext<Self>,
10561 ) -> Option<TransactionId> {
10562 if let Some(transaction_id) = self
10563 .buffer
10564 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
10565 {
10566 if let Some((_, end_selections)) =
10567 self.selection_history.transaction_mut(transaction_id)
10568 {
10569 *end_selections = Some(self.selections.disjoint_anchors());
10570 } else {
10571 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
10572 }
10573
10574 cx.emit(EditorEvent::Edited { transaction_id });
10575 Some(transaction_id)
10576 } else {
10577 None
10578 }
10579 }
10580
10581 pub fn toggle_fold(&mut self, _: &actions::ToggleFold, cx: &mut ViewContext<Self>) {
10582 if self.is_singleton(cx) {
10583 let selection = self.selections.newest::<Point>(cx);
10584
10585 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10586 let range = if selection.is_empty() {
10587 let point = selection.head().to_display_point(&display_map);
10588 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10589 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10590 .to_point(&display_map);
10591 start..end
10592 } else {
10593 selection.range()
10594 };
10595 if display_map.folds_in_range(range).next().is_some() {
10596 self.unfold_lines(&Default::default(), cx)
10597 } else {
10598 self.fold(&Default::default(), cx)
10599 }
10600 } else {
10601 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10602 let mut toggled_buffers = HashSet::default();
10603 for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
10604 self.selections
10605 .disjoint_anchors()
10606 .into_iter()
10607 .map(|selection| selection.range()),
10608 ) {
10609 let buffer_id = buffer_snapshot.remote_id();
10610 if toggled_buffers.insert(buffer_id) {
10611 if self.buffer_folded(buffer_id, cx) {
10612 self.unfold_buffer(buffer_id, cx);
10613 } else {
10614 self.fold_buffer(buffer_id, cx);
10615 }
10616 }
10617 }
10618 }
10619 }
10620
10621 pub fn toggle_fold_recursive(
10622 &mut self,
10623 _: &actions::ToggleFoldRecursive,
10624 cx: &mut ViewContext<Self>,
10625 ) {
10626 let selection = self.selections.newest::<Point>(cx);
10627
10628 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10629 let range = if selection.is_empty() {
10630 let point = selection.head().to_display_point(&display_map);
10631 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
10632 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
10633 .to_point(&display_map);
10634 start..end
10635 } else {
10636 selection.range()
10637 };
10638 if display_map.folds_in_range(range).next().is_some() {
10639 self.unfold_recursive(&Default::default(), cx)
10640 } else {
10641 self.fold_recursive(&Default::default(), cx)
10642 }
10643 }
10644
10645 pub fn fold(&mut self, _: &actions::Fold, cx: &mut ViewContext<Self>) {
10646 if self.is_singleton(cx) {
10647 let mut to_fold = Vec::new();
10648 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10649 let selections = self.selections.all_adjusted(cx);
10650
10651 for selection in selections {
10652 let range = selection.range().sorted();
10653 let buffer_start_row = range.start.row;
10654
10655 if range.start.row != range.end.row {
10656 let mut found = false;
10657 let mut row = range.start.row;
10658 while row <= range.end.row {
10659 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
10660 {
10661 found = true;
10662 row = crease.range().end.row + 1;
10663 to_fold.push(crease);
10664 } else {
10665 row += 1
10666 }
10667 }
10668 if found {
10669 continue;
10670 }
10671 }
10672
10673 for row in (0..=range.start.row).rev() {
10674 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10675 if crease.range().end.row >= buffer_start_row {
10676 to_fold.push(crease);
10677 if row <= range.start.row {
10678 break;
10679 }
10680 }
10681 }
10682 }
10683 }
10684
10685 self.fold_creases(to_fold, true, cx);
10686 } else {
10687 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10688 let mut folded_buffers = HashSet::default();
10689 for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
10690 self.selections
10691 .disjoint_anchors()
10692 .into_iter()
10693 .map(|selection| selection.range()),
10694 ) {
10695 let buffer_id = buffer_snapshot.remote_id();
10696 if folded_buffers.insert(buffer_id) {
10697 self.fold_buffer(buffer_id, cx);
10698 }
10699 }
10700 }
10701 }
10702
10703 fn fold_at_level(&mut self, fold_at: &FoldAtLevel, cx: &mut ViewContext<Self>) {
10704 if !self.buffer.read(cx).is_singleton() {
10705 return;
10706 }
10707
10708 let fold_at_level = fold_at.level;
10709 let snapshot = self.buffer.read(cx).snapshot(cx);
10710 let mut to_fold = Vec::new();
10711 let mut stack = vec![(0, snapshot.max_row().0, 1)];
10712
10713 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
10714 while start_row < end_row {
10715 match self
10716 .snapshot(cx)
10717 .crease_for_buffer_row(MultiBufferRow(start_row))
10718 {
10719 Some(crease) => {
10720 let nested_start_row = crease.range().start.row + 1;
10721 let nested_end_row = crease.range().end.row;
10722
10723 if current_level < fold_at_level {
10724 stack.push((nested_start_row, nested_end_row, current_level + 1));
10725 } else if current_level == fold_at_level {
10726 to_fold.push(crease);
10727 }
10728
10729 start_row = nested_end_row + 1;
10730 }
10731 None => start_row += 1,
10732 }
10733 }
10734 }
10735
10736 self.fold_creases(to_fold, true, cx);
10737 }
10738
10739 pub fn fold_all(&mut self, _: &actions::FoldAll, cx: &mut ViewContext<Self>) {
10740 if self.buffer.read(cx).is_singleton() {
10741 let mut fold_ranges = Vec::new();
10742 let snapshot = self.buffer.read(cx).snapshot(cx);
10743
10744 for row in 0..snapshot.max_row().0 {
10745 if let Some(foldable_range) =
10746 self.snapshot(cx).crease_for_buffer_row(MultiBufferRow(row))
10747 {
10748 fold_ranges.push(foldable_range);
10749 }
10750 }
10751
10752 self.fold_creases(fold_ranges, true, cx);
10753 } else {
10754 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
10755 editor
10756 .update(&mut cx, |editor, cx| {
10757 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
10758 editor.fold_buffer(buffer_id, cx);
10759 }
10760 })
10761 .ok();
10762 });
10763 }
10764 }
10765
10766 pub fn fold_function_bodies(
10767 &mut self,
10768 _: &actions::FoldFunctionBodies,
10769 cx: &mut ViewContext<Self>,
10770 ) {
10771 let snapshot = self.buffer.read(cx).snapshot(cx);
10772 let Some((_, _, buffer)) = snapshot.as_singleton() else {
10773 return;
10774 };
10775 let creases = buffer
10776 .function_body_fold_ranges(0..buffer.len())
10777 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
10778 .collect();
10779
10780 self.fold_creases(creases, true, cx);
10781 }
10782
10783 pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
10784 let mut to_fold = Vec::new();
10785 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10786 let selections = self.selections.all_adjusted(cx);
10787
10788 for selection in selections {
10789 let range = selection.range().sorted();
10790 let buffer_start_row = range.start.row;
10791
10792 if range.start.row != range.end.row {
10793 let mut found = false;
10794 for row in range.start.row..=range.end.row {
10795 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10796 found = true;
10797 to_fold.push(crease);
10798 }
10799 }
10800 if found {
10801 continue;
10802 }
10803 }
10804
10805 for row in (0..=range.start.row).rev() {
10806 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
10807 if crease.range().end.row >= buffer_start_row {
10808 to_fold.push(crease);
10809 } else {
10810 break;
10811 }
10812 }
10813 }
10814 }
10815
10816 self.fold_creases(to_fold, true, cx);
10817 }
10818
10819 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
10820 let buffer_row = fold_at.buffer_row;
10821 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10822
10823 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
10824 let autoscroll = self
10825 .selections
10826 .all::<Point>(cx)
10827 .iter()
10828 .any(|selection| crease.range().overlaps(&selection.range()));
10829
10830 self.fold_creases(vec![crease], autoscroll, cx);
10831 }
10832 }
10833
10834 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
10835 if self.is_singleton(cx) {
10836 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10837 let buffer = &display_map.buffer_snapshot;
10838 let selections = self.selections.all::<Point>(cx);
10839 let ranges = selections
10840 .iter()
10841 .map(|s| {
10842 let range = s.display_range(&display_map).sorted();
10843 let mut start = range.start.to_point(&display_map);
10844 let mut end = range.end.to_point(&display_map);
10845 start.column = 0;
10846 end.column = buffer.line_len(MultiBufferRow(end.row));
10847 start..end
10848 })
10849 .collect::<Vec<_>>();
10850
10851 self.unfold_ranges(&ranges, true, true, cx);
10852 } else {
10853 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
10854 let mut unfolded_buffers = HashSet::default();
10855 for (_, buffer_snapshot, _) in multi_buffer_snapshot.excerpts_in_ranges(
10856 self.selections
10857 .disjoint_anchors()
10858 .into_iter()
10859 .map(|selection| selection.range()),
10860 ) {
10861 let buffer_id = buffer_snapshot.remote_id();
10862 if unfolded_buffers.insert(buffer_id) {
10863 self.unfold_buffer(buffer_id, cx);
10864 }
10865 }
10866 }
10867 }
10868
10869 pub fn unfold_recursive(&mut self, _: &UnfoldRecursive, cx: &mut ViewContext<Self>) {
10870 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10871 let selections = self.selections.all::<Point>(cx);
10872 let ranges = selections
10873 .iter()
10874 .map(|s| {
10875 let mut range = s.display_range(&display_map).sorted();
10876 *range.start.column_mut() = 0;
10877 *range.end.column_mut() = display_map.line_len(range.end.row());
10878 let start = range.start.to_point(&display_map);
10879 let end = range.end.to_point(&display_map);
10880 start..end
10881 })
10882 .collect::<Vec<_>>();
10883
10884 self.unfold_ranges(&ranges, true, true, cx);
10885 }
10886
10887 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
10888 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10889
10890 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
10891 ..Point::new(
10892 unfold_at.buffer_row.0,
10893 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
10894 );
10895
10896 let autoscroll = self
10897 .selections
10898 .all::<Point>(cx)
10899 .iter()
10900 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
10901
10902 self.unfold_ranges(&[intersection_range], true, autoscroll, cx)
10903 }
10904
10905 pub fn unfold_all(&mut self, _: &actions::UnfoldAll, cx: &mut ViewContext<Self>) {
10906 if self.buffer.read(cx).is_singleton() {
10907 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10908 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
10909 } else {
10910 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
10911 editor
10912 .update(&mut cx, |editor, cx| {
10913 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
10914 editor.unfold_buffer(buffer_id, cx);
10915 }
10916 })
10917 .ok();
10918 });
10919 }
10920 }
10921
10922 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
10923 let selections = self.selections.all::<Point>(cx);
10924 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10925 let line_mode = self.selections.line_mode;
10926 let ranges = selections
10927 .into_iter()
10928 .map(|s| {
10929 if line_mode {
10930 let start = Point::new(s.start.row, 0);
10931 let end = Point::new(
10932 s.end.row,
10933 display_map
10934 .buffer_snapshot
10935 .line_len(MultiBufferRow(s.end.row)),
10936 );
10937 Crease::simple(start..end, display_map.fold_placeholder.clone())
10938 } else {
10939 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
10940 }
10941 })
10942 .collect::<Vec<_>>();
10943 self.fold_creases(ranges, true, cx);
10944 }
10945
10946 pub fn fold_creases<T: ToOffset + Clone>(
10947 &mut self,
10948 creases: Vec<Crease<T>>,
10949 auto_scroll: bool,
10950 cx: &mut ViewContext<Self>,
10951 ) {
10952 if creases.is_empty() {
10953 return;
10954 }
10955
10956 let mut buffers_affected = HashSet::default();
10957 let multi_buffer = self.buffer().read(cx);
10958 for crease in &creases {
10959 if let Some((_, buffer, _)) =
10960 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
10961 {
10962 buffers_affected.insert(buffer.read(cx).remote_id());
10963 };
10964 }
10965
10966 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
10967
10968 if auto_scroll {
10969 self.request_autoscroll(Autoscroll::fit(), cx);
10970 }
10971
10972 for buffer_id in buffers_affected {
10973 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
10974 }
10975
10976 cx.notify();
10977
10978 if let Some(active_diagnostics) = self.active_diagnostics.take() {
10979 // Clear diagnostics block when folding a range that contains it.
10980 let snapshot = self.snapshot(cx);
10981 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
10982 drop(snapshot);
10983 self.active_diagnostics = Some(active_diagnostics);
10984 self.dismiss_diagnostics(cx);
10985 } else {
10986 self.active_diagnostics = Some(active_diagnostics);
10987 }
10988 }
10989
10990 self.scrollbar_marker_state.dirty = true;
10991 }
10992
10993 /// Removes any folds whose ranges intersect any of the given ranges.
10994 pub fn unfold_ranges<T: ToOffset + Clone>(
10995 &mut self,
10996 ranges: &[Range<T>],
10997 inclusive: bool,
10998 auto_scroll: bool,
10999 cx: &mut ViewContext<Self>,
11000 ) {
11001 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11002 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
11003 });
11004 }
11005
11006 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut ViewContext<Self>) {
11007 if self.buffer().read(cx).is_singleton() || self.buffer_folded(buffer_id, cx) {
11008 return;
11009 }
11010 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
11011 return;
11012 };
11013 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(&buffer, cx);
11014 self.display_map
11015 .update(cx, |display_map, cx| display_map.fold_buffer(buffer_id, cx));
11016 cx.emit(EditorEvent::BufferFoldToggled {
11017 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
11018 folded: true,
11019 });
11020 cx.notify();
11021 }
11022
11023 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut ViewContext<Self>) {
11024 if self.buffer().read(cx).is_singleton() || !self.buffer_folded(buffer_id, cx) {
11025 return;
11026 }
11027 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
11028 return;
11029 };
11030 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(&buffer, cx);
11031 self.display_map.update(cx, |display_map, cx| {
11032 display_map.unfold_buffer(buffer_id, cx);
11033 });
11034 cx.emit(EditorEvent::BufferFoldToggled {
11035 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
11036 folded: false,
11037 });
11038 cx.notify();
11039 }
11040
11041 pub fn buffer_folded(&self, buffer: BufferId, cx: &AppContext) -> bool {
11042 self.display_map.read(cx).buffer_folded(buffer)
11043 }
11044
11045 /// Removes any folds with the given ranges.
11046 pub fn remove_folds_with_type<T: ToOffset + Clone>(
11047 &mut self,
11048 ranges: &[Range<T>],
11049 type_id: TypeId,
11050 auto_scroll: bool,
11051 cx: &mut ViewContext<Self>,
11052 ) {
11053 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
11054 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
11055 });
11056 }
11057
11058 fn remove_folds_with<T: ToOffset + Clone>(
11059 &mut self,
11060 ranges: &[Range<T>],
11061 auto_scroll: bool,
11062 cx: &mut ViewContext<Self>,
11063 update: impl FnOnce(&mut DisplayMap, &mut ModelContext<DisplayMap>),
11064 ) {
11065 if ranges.is_empty() {
11066 return;
11067 }
11068
11069 let mut buffers_affected = HashSet::default();
11070 let multi_buffer = self.buffer().read(cx);
11071 for range in ranges {
11072 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
11073 buffers_affected.insert(buffer.read(cx).remote_id());
11074 };
11075 }
11076
11077 self.display_map.update(cx, update);
11078
11079 if auto_scroll {
11080 self.request_autoscroll(Autoscroll::fit(), cx);
11081 }
11082
11083 for buffer_id in buffers_affected {
11084 Self::sync_expanded_diff_hunks(&mut self.diff_map, buffer_id, cx);
11085 }
11086
11087 cx.notify();
11088 self.scrollbar_marker_state.dirty = true;
11089 self.active_indent_guides_state.dirty = true;
11090 }
11091
11092 pub fn default_fold_placeholder(&self, cx: &AppContext) -> FoldPlaceholder {
11093 self.display_map.read(cx).fold_placeholder.clone()
11094 }
11095
11096 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut ViewContext<Self>) {
11097 if hovered != self.gutter_hovered {
11098 self.gutter_hovered = hovered;
11099 cx.notify();
11100 }
11101 }
11102
11103 pub fn insert_blocks(
11104 &mut self,
11105 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
11106 autoscroll: Option<Autoscroll>,
11107 cx: &mut ViewContext<Self>,
11108 ) -> Vec<CustomBlockId> {
11109 let blocks = self
11110 .display_map
11111 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
11112 if let Some(autoscroll) = autoscroll {
11113 self.request_autoscroll(autoscroll, cx);
11114 }
11115 cx.notify();
11116 blocks
11117 }
11118
11119 pub fn resize_blocks(
11120 &mut self,
11121 heights: HashMap<CustomBlockId, u32>,
11122 autoscroll: Option<Autoscroll>,
11123 cx: &mut ViewContext<Self>,
11124 ) {
11125 self.display_map
11126 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
11127 if let Some(autoscroll) = autoscroll {
11128 self.request_autoscroll(autoscroll, cx);
11129 }
11130 cx.notify();
11131 }
11132
11133 pub fn replace_blocks(
11134 &mut self,
11135 renderers: HashMap<CustomBlockId, RenderBlock>,
11136 autoscroll: Option<Autoscroll>,
11137 cx: &mut ViewContext<Self>,
11138 ) {
11139 self.display_map
11140 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
11141 if let Some(autoscroll) = autoscroll {
11142 self.request_autoscroll(autoscroll, cx);
11143 }
11144 cx.notify();
11145 }
11146
11147 pub fn remove_blocks(
11148 &mut self,
11149 block_ids: HashSet<CustomBlockId>,
11150 autoscroll: Option<Autoscroll>,
11151 cx: &mut ViewContext<Self>,
11152 ) {
11153 self.display_map.update(cx, |display_map, cx| {
11154 display_map.remove_blocks(block_ids, cx)
11155 });
11156 if let Some(autoscroll) = autoscroll {
11157 self.request_autoscroll(autoscroll, cx);
11158 }
11159 cx.notify();
11160 }
11161
11162 pub fn row_for_block(
11163 &self,
11164 block_id: CustomBlockId,
11165 cx: &mut ViewContext<Self>,
11166 ) -> Option<DisplayRow> {
11167 self.display_map
11168 .update(cx, |map, cx| map.row_for_block(block_id, cx))
11169 }
11170
11171 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
11172 self.focused_block = Some(focused_block);
11173 }
11174
11175 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
11176 self.focused_block.take()
11177 }
11178
11179 pub fn insert_creases(
11180 &mut self,
11181 creases: impl IntoIterator<Item = Crease<Anchor>>,
11182 cx: &mut ViewContext<Self>,
11183 ) -> Vec<CreaseId> {
11184 self.display_map
11185 .update(cx, |map, cx| map.insert_creases(creases, cx))
11186 }
11187
11188 pub fn remove_creases(
11189 &mut self,
11190 ids: impl IntoIterator<Item = CreaseId>,
11191 cx: &mut ViewContext<Self>,
11192 ) {
11193 self.display_map
11194 .update(cx, |map, cx| map.remove_creases(ids, cx));
11195 }
11196
11197 pub fn longest_row(&self, cx: &mut AppContext) -> DisplayRow {
11198 self.display_map
11199 .update(cx, |map, cx| map.snapshot(cx))
11200 .longest_row()
11201 }
11202
11203 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
11204 self.display_map
11205 .update(cx, |map, cx| map.snapshot(cx))
11206 .max_point()
11207 }
11208
11209 pub fn text(&self, cx: &AppContext) -> String {
11210 self.buffer.read(cx).read(cx).text()
11211 }
11212
11213 pub fn text_option(&self, cx: &AppContext) -> Option<String> {
11214 let text = self.text(cx);
11215 let text = text.trim();
11216
11217 if text.is_empty() {
11218 return None;
11219 }
11220
11221 Some(text.to_string())
11222 }
11223
11224 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
11225 self.transact(cx, |this, cx| {
11226 this.buffer
11227 .read(cx)
11228 .as_singleton()
11229 .expect("you can only call set_text on editors for singleton buffers")
11230 .update(cx, |buffer, cx| buffer.set_text(text, cx));
11231 });
11232 }
11233
11234 pub fn display_text(&self, cx: &mut AppContext) -> String {
11235 self.display_map
11236 .update(cx, |map, cx| map.snapshot(cx))
11237 .text()
11238 }
11239
11240 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
11241 let mut wrap_guides = smallvec::smallvec![];
11242
11243 if self.show_wrap_guides == Some(false) {
11244 return wrap_guides;
11245 }
11246
11247 let settings = self.buffer.read(cx).settings_at(0, cx);
11248 if settings.show_wrap_guides {
11249 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
11250 wrap_guides.push((soft_wrap as usize, true));
11251 } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) {
11252 wrap_guides.push((soft_wrap as usize, true));
11253 }
11254 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
11255 }
11256
11257 wrap_guides
11258 }
11259
11260 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
11261 let settings = self.buffer.read(cx).settings_at(0, cx);
11262 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
11263 match mode {
11264 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
11265 SoftWrap::None
11266 }
11267 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
11268 language_settings::SoftWrap::PreferredLineLength => {
11269 SoftWrap::Column(settings.preferred_line_length)
11270 }
11271 language_settings::SoftWrap::Bounded => {
11272 SoftWrap::Bounded(settings.preferred_line_length)
11273 }
11274 }
11275 }
11276
11277 pub fn set_soft_wrap_mode(
11278 &mut self,
11279 mode: language_settings::SoftWrap,
11280 cx: &mut ViewContext<Self>,
11281 ) {
11282 self.soft_wrap_mode_override = Some(mode);
11283 cx.notify();
11284 }
11285
11286 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
11287 self.text_style_refinement = Some(style);
11288 }
11289
11290 /// called by the Element so we know what style we were most recently rendered with.
11291 pub(crate) fn set_style(&mut self, style: EditorStyle, cx: &mut ViewContext<Self>) {
11292 let rem_size = cx.rem_size();
11293 self.display_map.update(cx, |map, cx| {
11294 map.set_font(
11295 style.text.font(),
11296 style.text.font_size.to_pixels(rem_size),
11297 cx,
11298 )
11299 });
11300 self.style = Some(style);
11301 }
11302
11303 pub fn style(&self) -> Option<&EditorStyle> {
11304 self.style.as_ref()
11305 }
11306
11307 // Called by the element. This method is not designed to be called outside of the editor
11308 // element's layout code because it does not notify when rewrapping is computed synchronously.
11309 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut AppContext) -> bool {
11310 self.display_map
11311 .update(cx, |map, cx| map.set_wrap_width(width, cx))
11312 }
11313
11314 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
11315 if self.soft_wrap_mode_override.is_some() {
11316 self.soft_wrap_mode_override.take();
11317 } else {
11318 let soft_wrap = match self.soft_wrap_mode(cx) {
11319 SoftWrap::GitDiff => return,
11320 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
11321 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
11322 language_settings::SoftWrap::None
11323 }
11324 };
11325 self.soft_wrap_mode_override = Some(soft_wrap);
11326 }
11327 cx.notify();
11328 }
11329
11330 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, cx: &mut ViewContext<Self>) {
11331 let Some(workspace) = self.workspace() else {
11332 return;
11333 };
11334 let fs = workspace.read(cx).app_state().fs.clone();
11335 let current_show = TabBarSettings::get_global(cx).show;
11336 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
11337 setting.show = Some(!current_show);
11338 });
11339 }
11340
11341 pub fn toggle_indent_guides(&mut self, _: &ToggleIndentGuides, cx: &mut ViewContext<Self>) {
11342 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
11343 self.buffer
11344 .read(cx)
11345 .settings_at(0, cx)
11346 .indent_guides
11347 .enabled
11348 });
11349 self.show_indent_guides = Some(!currently_enabled);
11350 cx.notify();
11351 }
11352
11353 fn should_show_indent_guides(&self) -> Option<bool> {
11354 self.show_indent_guides
11355 }
11356
11357 pub fn toggle_line_numbers(&mut self, _: &ToggleLineNumbers, cx: &mut ViewContext<Self>) {
11358 let mut editor_settings = EditorSettings::get_global(cx).clone();
11359 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
11360 EditorSettings::override_global(editor_settings, cx);
11361 }
11362
11363 pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool {
11364 self.use_relative_line_numbers
11365 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
11366 }
11367
11368 pub fn toggle_relative_line_numbers(
11369 &mut self,
11370 _: &ToggleRelativeLineNumbers,
11371 cx: &mut ViewContext<Self>,
11372 ) {
11373 let is_relative = self.should_use_relative_line_numbers(cx);
11374 self.set_relative_line_number(Some(!is_relative), cx)
11375 }
11376
11377 pub fn set_relative_line_number(
11378 &mut self,
11379 is_relative: Option<bool>,
11380 cx: &mut ViewContext<Self>,
11381 ) {
11382 self.use_relative_line_numbers = is_relative;
11383 cx.notify();
11384 }
11385
11386 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
11387 self.show_gutter = show_gutter;
11388 cx.notify();
11389 }
11390
11391 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut ViewContext<Self>) {
11392 self.show_scrollbars = show_scrollbars;
11393 cx.notify();
11394 }
11395
11396 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut ViewContext<Self>) {
11397 self.show_line_numbers = Some(show_line_numbers);
11398 cx.notify();
11399 }
11400
11401 pub fn set_show_git_diff_gutter(
11402 &mut self,
11403 show_git_diff_gutter: bool,
11404 cx: &mut ViewContext<Self>,
11405 ) {
11406 self.show_git_diff_gutter = Some(show_git_diff_gutter);
11407 cx.notify();
11408 }
11409
11410 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut ViewContext<Self>) {
11411 self.show_code_actions = Some(show_code_actions);
11412 cx.notify();
11413 }
11414
11415 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut ViewContext<Self>) {
11416 self.show_runnables = Some(show_runnables);
11417 cx.notify();
11418 }
11419
11420 pub fn set_masked(&mut self, masked: bool, cx: &mut ViewContext<Self>) {
11421 if self.display_map.read(cx).masked != masked {
11422 self.display_map.update(cx, |map, _| map.masked = masked);
11423 }
11424 cx.notify()
11425 }
11426
11427 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut ViewContext<Self>) {
11428 self.show_wrap_guides = Some(show_wrap_guides);
11429 cx.notify();
11430 }
11431
11432 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut ViewContext<Self>) {
11433 self.show_indent_guides = Some(show_indent_guides);
11434 cx.notify();
11435 }
11436
11437 pub fn working_directory(&self, cx: &WindowContext) -> Option<PathBuf> {
11438 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11439 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
11440 if let Some(dir) = file.abs_path(cx).parent() {
11441 return Some(dir.to_owned());
11442 }
11443 }
11444
11445 if let Some(project_path) = buffer.read(cx).project_path(cx) {
11446 return Some(project_path.path.to_path_buf());
11447 }
11448 }
11449
11450 None
11451 }
11452
11453 fn target_file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn language::LocalFile> {
11454 self.active_excerpt(cx)?
11455 .1
11456 .read(cx)
11457 .file()
11458 .and_then(|f| f.as_local())
11459 }
11460
11461 pub fn reveal_in_finder(&mut self, _: &RevealInFileManager, cx: &mut ViewContext<Self>) {
11462 if let Some(target) = self.target_file(cx) {
11463 cx.reveal_path(&target.abs_path(cx));
11464 }
11465 }
11466
11467 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
11468 if let Some(file) = self.target_file(cx) {
11469 if let Some(path) = file.abs_path(cx).to_str() {
11470 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11471 }
11472 }
11473 }
11474
11475 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
11476 if let Some(file) = self.target_file(cx) {
11477 if let Some(path) = file.path().to_str() {
11478 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
11479 }
11480 }
11481 }
11482
11483 pub fn toggle_git_blame(&mut self, _: &ToggleGitBlame, cx: &mut ViewContext<Self>) {
11484 self.show_git_blame_gutter = !self.show_git_blame_gutter;
11485
11486 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
11487 self.start_git_blame(true, cx);
11488 }
11489
11490 cx.notify();
11491 }
11492
11493 pub fn toggle_git_blame_inline(
11494 &mut self,
11495 _: &ToggleGitBlameInline,
11496 cx: &mut ViewContext<Self>,
11497 ) {
11498 self.toggle_git_blame_inline_internal(true, cx);
11499 cx.notify();
11500 }
11501
11502 pub fn git_blame_inline_enabled(&self) -> bool {
11503 self.git_blame_inline_enabled
11504 }
11505
11506 pub fn toggle_selection_menu(&mut self, _: &ToggleSelectionMenu, cx: &mut ViewContext<Self>) {
11507 self.show_selection_menu = self
11508 .show_selection_menu
11509 .map(|show_selections_menu| !show_selections_menu)
11510 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
11511
11512 cx.notify();
11513 }
11514
11515 pub fn selection_menu_enabled(&self, cx: &AppContext) -> bool {
11516 self.show_selection_menu
11517 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
11518 }
11519
11520 fn start_git_blame(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11521 if let Some(project) = self.project.as_ref() {
11522 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
11523 return;
11524 };
11525
11526 if buffer.read(cx).file().is_none() {
11527 return;
11528 }
11529
11530 let focused = self.focus_handle(cx).contains_focused(cx);
11531
11532 let project = project.clone();
11533 let blame =
11534 cx.new_model(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
11535 self.blame_subscription = Some(cx.observe(&blame, |_, _, cx| cx.notify()));
11536 self.blame = Some(blame);
11537 }
11538 }
11539
11540 fn toggle_git_blame_inline_internal(
11541 &mut self,
11542 user_triggered: bool,
11543 cx: &mut ViewContext<Self>,
11544 ) {
11545 if self.git_blame_inline_enabled {
11546 self.git_blame_inline_enabled = false;
11547 self.show_git_blame_inline = false;
11548 self.show_git_blame_inline_delay_task.take();
11549 } else {
11550 self.git_blame_inline_enabled = true;
11551 self.start_git_blame_inline(user_triggered, cx);
11552 }
11553
11554 cx.notify();
11555 }
11556
11557 fn start_git_blame_inline(&mut self, user_triggered: bool, cx: &mut ViewContext<Self>) {
11558 self.start_git_blame(user_triggered, cx);
11559
11560 if ProjectSettings::get_global(cx)
11561 .git
11562 .inline_blame_delay()
11563 .is_some()
11564 {
11565 self.start_inline_blame_timer(cx);
11566 } else {
11567 self.show_git_blame_inline = true
11568 }
11569 }
11570
11571 pub fn blame(&self) -> Option<&Model<GitBlame>> {
11572 self.blame.as_ref()
11573 }
11574
11575 pub fn show_git_blame_gutter(&self) -> bool {
11576 self.show_git_blame_gutter
11577 }
11578
11579 pub fn render_git_blame_gutter(&mut self, cx: &mut WindowContext) -> bool {
11580 self.show_git_blame_gutter && self.has_blame_entries(cx)
11581 }
11582
11583 pub fn render_git_blame_inline(&mut self, cx: &mut WindowContext) -> bool {
11584 self.show_git_blame_inline
11585 && self.focus_handle.is_focused(cx)
11586 && !self.newest_selection_head_on_empty_line(cx)
11587 && self.has_blame_entries(cx)
11588 }
11589
11590 fn has_blame_entries(&self, cx: &mut WindowContext) -> bool {
11591 self.blame()
11592 .map_or(false, |blame| blame.read(cx).has_generated_entries())
11593 }
11594
11595 fn newest_selection_head_on_empty_line(&mut self, cx: &mut WindowContext) -> bool {
11596 let cursor_anchor = self.selections.newest_anchor().head();
11597
11598 let snapshot = self.buffer.read(cx).snapshot(cx);
11599 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
11600
11601 snapshot.line_len(buffer_row) == 0
11602 }
11603
11604 fn get_permalink_to_line(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<url::Url>> {
11605 let buffer_and_selection = maybe!({
11606 let selection = self.selections.newest::<Point>(cx);
11607 let selection_range = selection.range();
11608
11609 let (buffer, selection) = if let Some(buffer) = self.buffer().read(cx).as_singleton() {
11610 (buffer, selection_range.start.row..selection_range.end.row)
11611 } else {
11612 let multi_buffer = self.buffer().read(cx);
11613 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
11614 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
11615
11616 let (excerpt, range) = if selection.reversed {
11617 buffer_ranges.first()
11618 } else {
11619 buffer_ranges.last()
11620 }?;
11621
11622 let snapshot = excerpt.buffer();
11623 let selection = text::ToPoint::to_point(&range.start, &snapshot).row
11624 ..text::ToPoint::to_point(&range.end, &snapshot).row;
11625 (
11626 multi_buffer.buffer(excerpt.buffer_id()).unwrap().clone(),
11627 selection,
11628 )
11629 };
11630
11631 Some((buffer, selection))
11632 });
11633
11634 let Some((buffer, selection)) = buffer_and_selection else {
11635 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
11636 };
11637
11638 let Some(project) = self.project.as_ref() else {
11639 return Task::ready(Err(anyhow!("editor does not have project")));
11640 };
11641
11642 project.update(cx, |project, cx| {
11643 project.get_permalink_to_line(&buffer, selection, cx)
11644 })
11645 }
11646
11647 pub fn copy_permalink_to_line(&mut self, _: &CopyPermalinkToLine, cx: &mut ViewContext<Self>) {
11648 let permalink_task = self.get_permalink_to_line(cx);
11649 let workspace = self.workspace();
11650
11651 cx.spawn(|_, mut cx| async move {
11652 match permalink_task.await {
11653 Ok(permalink) => {
11654 cx.update(|cx| {
11655 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
11656 })
11657 .ok();
11658 }
11659 Err(err) => {
11660 let message = format!("Failed to copy permalink: {err}");
11661
11662 Err::<(), anyhow::Error>(err).log_err();
11663
11664 if let Some(workspace) = workspace {
11665 workspace
11666 .update(&mut cx, |workspace, cx| {
11667 struct CopyPermalinkToLine;
11668
11669 workspace.show_toast(
11670 Toast::new(
11671 NotificationId::unique::<CopyPermalinkToLine>(),
11672 message,
11673 ),
11674 cx,
11675 )
11676 })
11677 .ok();
11678 }
11679 }
11680 }
11681 })
11682 .detach();
11683 }
11684
11685 pub fn copy_file_location(&mut self, _: &CopyFileLocation, cx: &mut ViewContext<Self>) {
11686 let selection = self.selections.newest::<Point>(cx).start.row + 1;
11687 if let Some(file) = self.target_file(cx) {
11688 if let Some(path) = file.path().to_str() {
11689 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
11690 }
11691 }
11692 }
11693
11694 pub fn open_permalink_to_line(&mut self, _: &OpenPermalinkToLine, cx: &mut ViewContext<Self>) {
11695 let permalink_task = self.get_permalink_to_line(cx);
11696 let workspace = self.workspace();
11697
11698 cx.spawn(|_, mut cx| async move {
11699 match permalink_task.await {
11700 Ok(permalink) => {
11701 cx.update(|cx| {
11702 cx.open_url(permalink.as_ref());
11703 })
11704 .ok();
11705 }
11706 Err(err) => {
11707 let message = format!("Failed to open permalink: {err}");
11708
11709 Err::<(), anyhow::Error>(err).log_err();
11710
11711 if let Some(workspace) = workspace {
11712 workspace
11713 .update(&mut cx, |workspace, cx| {
11714 struct OpenPermalinkToLine;
11715
11716 workspace.show_toast(
11717 Toast::new(
11718 NotificationId::unique::<OpenPermalinkToLine>(),
11719 message,
11720 ),
11721 cx,
11722 )
11723 })
11724 .ok();
11725 }
11726 }
11727 }
11728 })
11729 .detach();
11730 }
11731
11732 pub fn insert_uuid_v4(&mut self, _: &InsertUuidV4, cx: &mut ViewContext<Self>) {
11733 self.insert_uuid(UuidVersion::V4, cx);
11734 }
11735
11736 pub fn insert_uuid_v7(&mut self, _: &InsertUuidV7, cx: &mut ViewContext<Self>) {
11737 self.insert_uuid(UuidVersion::V7, cx);
11738 }
11739
11740 fn insert_uuid(&mut self, version: UuidVersion, cx: &mut ViewContext<Self>) {
11741 self.transact(cx, |this, cx| {
11742 let edits = this
11743 .selections
11744 .all::<Point>(cx)
11745 .into_iter()
11746 .map(|selection| {
11747 let uuid = match version {
11748 UuidVersion::V4 => uuid::Uuid::new_v4(),
11749 UuidVersion::V7 => uuid::Uuid::now_v7(),
11750 };
11751
11752 (selection.range(), uuid.to_string())
11753 });
11754 this.edit(edits, cx);
11755 this.refresh_inline_completion(true, false, cx);
11756 });
11757 }
11758
11759 /// Adds a row highlight for the given range. If a row has multiple highlights, the
11760 /// last highlight added will be used.
11761 ///
11762 /// If the range ends at the beginning of a line, then that line will not be highlighted.
11763 pub fn highlight_rows<T: 'static>(
11764 &mut self,
11765 range: Range<Anchor>,
11766 color: Hsla,
11767 should_autoscroll: bool,
11768 cx: &mut ViewContext<Self>,
11769 ) {
11770 let snapshot = self.buffer().read(cx).snapshot(cx);
11771 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11772 let ix = row_highlights.binary_search_by(|highlight| {
11773 Ordering::Equal
11774 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
11775 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
11776 });
11777
11778 if let Err(mut ix) = ix {
11779 let index = post_inc(&mut self.highlight_order);
11780
11781 // If this range intersects with the preceding highlight, then merge it with
11782 // the preceding highlight. Otherwise insert a new highlight.
11783 let mut merged = false;
11784 if ix > 0 {
11785 let prev_highlight = &mut row_highlights[ix - 1];
11786 if prev_highlight
11787 .range
11788 .end
11789 .cmp(&range.start, &snapshot)
11790 .is_ge()
11791 {
11792 ix -= 1;
11793 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
11794 prev_highlight.range.end = range.end;
11795 }
11796 merged = true;
11797 prev_highlight.index = index;
11798 prev_highlight.color = color;
11799 prev_highlight.should_autoscroll = should_autoscroll;
11800 }
11801 }
11802
11803 if !merged {
11804 row_highlights.insert(
11805 ix,
11806 RowHighlight {
11807 range: range.clone(),
11808 index,
11809 color,
11810 should_autoscroll,
11811 },
11812 );
11813 }
11814
11815 // If any of the following highlights intersect with this one, merge them.
11816 while let Some(next_highlight) = row_highlights.get(ix + 1) {
11817 let highlight = &row_highlights[ix];
11818 if next_highlight
11819 .range
11820 .start
11821 .cmp(&highlight.range.end, &snapshot)
11822 .is_le()
11823 {
11824 if next_highlight
11825 .range
11826 .end
11827 .cmp(&highlight.range.end, &snapshot)
11828 .is_gt()
11829 {
11830 row_highlights[ix].range.end = next_highlight.range.end;
11831 }
11832 row_highlights.remove(ix + 1);
11833 } else {
11834 break;
11835 }
11836 }
11837 }
11838 }
11839
11840 /// Remove any highlighted row ranges of the given type that intersect the
11841 /// given ranges.
11842 pub fn remove_highlighted_rows<T: 'static>(
11843 &mut self,
11844 ranges_to_remove: Vec<Range<Anchor>>,
11845 cx: &mut ViewContext<Self>,
11846 ) {
11847 let snapshot = self.buffer().read(cx).snapshot(cx);
11848 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
11849 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
11850 row_highlights.retain(|highlight| {
11851 while let Some(range_to_remove) = ranges_to_remove.peek() {
11852 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
11853 Ordering::Less | Ordering::Equal => {
11854 ranges_to_remove.next();
11855 }
11856 Ordering::Greater => {
11857 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
11858 Ordering::Less | Ordering::Equal => {
11859 return false;
11860 }
11861 Ordering::Greater => break,
11862 }
11863 }
11864 }
11865 }
11866
11867 true
11868 })
11869 }
11870
11871 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
11872 pub fn clear_row_highlights<T: 'static>(&mut self) {
11873 self.highlighted_rows.remove(&TypeId::of::<T>());
11874 }
11875
11876 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
11877 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
11878 self.highlighted_rows
11879 .get(&TypeId::of::<T>())
11880 .map_or(&[] as &[_], |vec| vec.as_slice())
11881 .iter()
11882 .map(|highlight| (highlight.range.clone(), highlight.color))
11883 }
11884
11885 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
11886 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
11887 /// Allows to ignore certain kinds of highlights.
11888 pub fn highlighted_display_rows(
11889 &mut self,
11890 cx: &mut WindowContext,
11891 ) -> BTreeMap<DisplayRow, Hsla> {
11892 let snapshot = self.snapshot(cx);
11893 let mut used_highlight_orders = HashMap::default();
11894 self.highlighted_rows
11895 .iter()
11896 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
11897 .fold(
11898 BTreeMap::<DisplayRow, Hsla>::new(),
11899 |mut unique_rows, highlight| {
11900 let start = highlight.range.start.to_display_point(&snapshot);
11901 let end = highlight.range.end.to_display_point(&snapshot);
11902 let start_row = start.row().0;
11903 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
11904 && end.column() == 0
11905 {
11906 end.row().0.saturating_sub(1)
11907 } else {
11908 end.row().0
11909 };
11910 for row in start_row..=end_row {
11911 let used_index =
11912 used_highlight_orders.entry(row).or_insert(highlight.index);
11913 if highlight.index >= *used_index {
11914 *used_index = highlight.index;
11915 unique_rows.insert(DisplayRow(row), highlight.color);
11916 }
11917 }
11918 unique_rows
11919 },
11920 )
11921 }
11922
11923 pub fn highlighted_display_row_for_autoscroll(
11924 &self,
11925 snapshot: &DisplaySnapshot,
11926 ) -> Option<DisplayRow> {
11927 self.highlighted_rows
11928 .values()
11929 .flat_map(|highlighted_rows| highlighted_rows.iter())
11930 .filter_map(|highlight| {
11931 if highlight.should_autoscroll {
11932 Some(highlight.range.start.to_display_point(snapshot).row())
11933 } else {
11934 None
11935 }
11936 })
11937 .min()
11938 }
11939
11940 pub fn set_search_within_ranges(
11941 &mut self,
11942 ranges: &[Range<Anchor>],
11943 cx: &mut ViewContext<Self>,
11944 ) {
11945 self.highlight_background::<SearchWithinRange>(
11946 ranges,
11947 |colors| colors.editor_document_highlight_read_background,
11948 cx,
11949 )
11950 }
11951
11952 pub fn set_breadcrumb_header(&mut self, new_header: String) {
11953 self.breadcrumb_header = Some(new_header);
11954 }
11955
11956 pub fn clear_search_within_ranges(&mut self, cx: &mut ViewContext<Self>) {
11957 self.clear_background_highlights::<SearchWithinRange>(cx);
11958 }
11959
11960 pub fn highlight_background<T: 'static>(
11961 &mut self,
11962 ranges: &[Range<Anchor>],
11963 color_fetcher: fn(&ThemeColors) -> Hsla,
11964 cx: &mut ViewContext<Self>,
11965 ) {
11966 self.background_highlights
11967 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11968 self.scrollbar_marker_state.dirty = true;
11969 cx.notify();
11970 }
11971
11972 pub fn clear_background_highlights<T: 'static>(
11973 &mut self,
11974 cx: &mut ViewContext<Self>,
11975 ) -> Option<BackgroundHighlight> {
11976 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
11977 if !text_highlights.1.is_empty() {
11978 self.scrollbar_marker_state.dirty = true;
11979 cx.notify();
11980 }
11981 Some(text_highlights)
11982 }
11983
11984 pub fn highlight_gutter<T: 'static>(
11985 &mut self,
11986 ranges: &[Range<Anchor>],
11987 color_fetcher: fn(&AppContext) -> Hsla,
11988 cx: &mut ViewContext<Self>,
11989 ) {
11990 self.gutter_highlights
11991 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
11992 cx.notify();
11993 }
11994
11995 pub fn clear_gutter_highlights<T: 'static>(
11996 &mut self,
11997 cx: &mut ViewContext<Self>,
11998 ) -> Option<GutterHighlight> {
11999 cx.notify();
12000 self.gutter_highlights.remove(&TypeId::of::<T>())
12001 }
12002
12003 #[cfg(feature = "test-support")]
12004 pub fn all_text_background_highlights(
12005 &mut self,
12006 cx: &mut ViewContext<Self>,
12007 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12008 let snapshot = self.snapshot(cx);
12009 let buffer = &snapshot.buffer_snapshot;
12010 let start = buffer.anchor_before(0);
12011 let end = buffer.anchor_after(buffer.len());
12012 let theme = cx.theme().colors();
12013 self.background_highlights_in_range(start..end, &snapshot, theme)
12014 }
12015
12016 #[cfg(feature = "test-support")]
12017 pub fn search_background_highlights(
12018 &mut self,
12019 cx: &mut ViewContext<Self>,
12020 ) -> Vec<Range<Point>> {
12021 let snapshot = self.buffer().read(cx).snapshot(cx);
12022
12023 let highlights = self
12024 .background_highlights
12025 .get(&TypeId::of::<items::BufferSearchHighlights>());
12026
12027 if let Some((_color, ranges)) = highlights {
12028 ranges
12029 .iter()
12030 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
12031 .collect_vec()
12032 } else {
12033 vec![]
12034 }
12035 }
12036
12037 fn document_highlights_for_position<'a>(
12038 &'a self,
12039 position: Anchor,
12040 buffer: &'a MultiBufferSnapshot,
12041 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
12042 let read_highlights = self
12043 .background_highlights
12044 .get(&TypeId::of::<DocumentHighlightRead>())
12045 .map(|h| &h.1);
12046 let write_highlights = self
12047 .background_highlights
12048 .get(&TypeId::of::<DocumentHighlightWrite>())
12049 .map(|h| &h.1);
12050 let left_position = position.bias_left(buffer);
12051 let right_position = position.bias_right(buffer);
12052 read_highlights
12053 .into_iter()
12054 .chain(write_highlights)
12055 .flat_map(move |ranges| {
12056 let start_ix = match ranges.binary_search_by(|probe| {
12057 let cmp = probe.end.cmp(&left_position, buffer);
12058 if cmp.is_ge() {
12059 Ordering::Greater
12060 } else {
12061 Ordering::Less
12062 }
12063 }) {
12064 Ok(i) | Err(i) => i,
12065 };
12066
12067 ranges[start_ix..]
12068 .iter()
12069 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
12070 })
12071 }
12072
12073 pub fn has_background_highlights<T: 'static>(&self) -> bool {
12074 self.background_highlights
12075 .get(&TypeId::of::<T>())
12076 .map_or(false, |(_, highlights)| !highlights.is_empty())
12077 }
12078
12079 pub fn background_highlights_in_range(
12080 &self,
12081 search_range: Range<Anchor>,
12082 display_snapshot: &DisplaySnapshot,
12083 theme: &ThemeColors,
12084 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12085 let mut results = Vec::new();
12086 for (color_fetcher, ranges) in self.background_highlights.values() {
12087 let color = color_fetcher(theme);
12088 let start_ix = match ranges.binary_search_by(|probe| {
12089 let cmp = probe
12090 .end
12091 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12092 if cmp.is_gt() {
12093 Ordering::Greater
12094 } else {
12095 Ordering::Less
12096 }
12097 }) {
12098 Ok(i) | Err(i) => i,
12099 };
12100 for range in &ranges[start_ix..] {
12101 if range
12102 .start
12103 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12104 .is_ge()
12105 {
12106 break;
12107 }
12108
12109 let start = range.start.to_display_point(display_snapshot);
12110 let end = range.end.to_display_point(display_snapshot);
12111 results.push((start..end, color))
12112 }
12113 }
12114 results
12115 }
12116
12117 pub fn background_highlight_row_ranges<T: 'static>(
12118 &self,
12119 search_range: Range<Anchor>,
12120 display_snapshot: &DisplaySnapshot,
12121 count: usize,
12122 ) -> Vec<RangeInclusive<DisplayPoint>> {
12123 let mut results = Vec::new();
12124 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
12125 return vec![];
12126 };
12127
12128 let start_ix = match ranges.binary_search_by(|probe| {
12129 let cmp = probe
12130 .end
12131 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12132 if cmp.is_gt() {
12133 Ordering::Greater
12134 } else {
12135 Ordering::Less
12136 }
12137 }) {
12138 Ok(i) | Err(i) => i,
12139 };
12140 let mut push_region = |start: Option<Point>, end: Option<Point>| {
12141 if let (Some(start_display), Some(end_display)) = (start, end) {
12142 results.push(
12143 start_display.to_display_point(display_snapshot)
12144 ..=end_display.to_display_point(display_snapshot),
12145 );
12146 }
12147 };
12148 let mut start_row: Option<Point> = None;
12149 let mut end_row: Option<Point> = None;
12150 if ranges.len() > count {
12151 return Vec::new();
12152 }
12153 for range in &ranges[start_ix..] {
12154 if range
12155 .start
12156 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12157 .is_ge()
12158 {
12159 break;
12160 }
12161 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
12162 if let Some(current_row) = &end_row {
12163 if end.row == current_row.row {
12164 continue;
12165 }
12166 }
12167 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
12168 if start_row.is_none() {
12169 assert_eq!(end_row, None);
12170 start_row = Some(start);
12171 end_row = Some(end);
12172 continue;
12173 }
12174 if let Some(current_end) = end_row.as_mut() {
12175 if start.row > current_end.row + 1 {
12176 push_region(start_row, end_row);
12177 start_row = Some(start);
12178 end_row = Some(end);
12179 } else {
12180 // Merge two hunks.
12181 *current_end = end;
12182 }
12183 } else {
12184 unreachable!();
12185 }
12186 }
12187 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
12188 push_region(start_row, end_row);
12189 results
12190 }
12191
12192 pub fn gutter_highlights_in_range(
12193 &self,
12194 search_range: Range<Anchor>,
12195 display_snapshot: &DisplaySnapshot,
12196 cx: &AppContext,
12197 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
12198 let mut results = Vec::new();
12199 for (color_fetcher, ranges) in self.gutter_highlights.values() {
12200 let color = color_fetcher(cx);
12201 let start_ix = match ranges.binary_search_by(|probe| {
12202 let cmp = probe
12203 .end
12204 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
12205 if cmp.is_gt() {
12206 Ordering::Greater
12207 } else {
12208 Ordering::Less
12209 }
12210 }) {
12211 Ok(i) | Err(i) => i,
12212 };
12213 for range in &ranges[start_ix..] {
12214 if range
12215 .start
12216 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
12217 .is_ge()
12218 {
12219 break;
12220 }
12221
12222 let start = range.start.to_display_point(display_snapshot);
12223 let end = range.end.to_display_point(display_snapshot);
12224 results.push((start..end, color))
12225 }
12226 }
12227 results
12228 }
12229
12230 /// Get the text ranges corresponding to the redaction query
12231 pub fn redacted_ranges(
12232 &self,
12233 search_range: Range<Anchor>,
12234 display_snapshot: &DisplaySnapshot,
12235 cx: &WindowContext,
12236 ) -> Vec<Range<DisplayPoint>> {
12237 display_snapshot
12238 .buffer_snapshot
12239 .redacted_ranges(search_range, |file| {
12240 if let Some(file) = file {
12241 file.is_private()
12242 && EditorSettings::get(
12243 Some(SettingsLocation {
12244 worktree_id: file.worktree_id(cx),
12245 path: file.path().as_ref(),
12246 }),
12247 cx,
12248 )
12249 .redact_private_values
12250 } else {
12251 false
12252 }
12253 })
12254 .map(|range| {
12255 range.start.to_display_point(display_snapshot)
12256 ..range.end.to_display_point(display_snapshot)
12257 })
12258 .collect()
12259 }
12260
12261 pub fn highlight_text<T: 'static>(
12262 &mut self,
12263 ranges: Vec<Range<Anchor>>,
12264 style: HighlightStyle,
12265 cx: &mut ViewContext<Self>,
12266 ) {
12267 self.display_map.update(cx, |map, _| {
12268 map.highlight_text(TypeId::of::<T>(), ranges, style)
12269 });
12270 cx.notify();
12271 }
12272
12273 pub(crate) fn highlight_inlays<T: 'static>(
12274 &mut self,
12275 highlights: Vec<InlayHighlight>,
12276 style: HighlightStyle,
12277 cx: &mut ViewContext<Self>,
12278 ) {
12279 self.display_map.update(cx, |map, _| {
12280 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
12281 });
12282 cx.notify();
12283 }
12284
12285 pub fn text_highlights<'a, T: 'static>(
12286 &'a self,
12287 cx: &'a AppContext,
12288 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
12289 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
12290 }
12291
12292 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
12293 let cleared = self
12294 .display_map
12295 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
12296 if cleared {
12297 cx.notify();
12298 }
12299 }
12300
12301 pub fn show_local_cursors(&self, cx: &WindowContext) -> bool {
12302 (self.read_only(cx) || self.blink_manager.read(cx).visible())
12303 && self.focus_handle.is_focused(cx)
12304 }
12305
12306 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut ViewContext<Self>) {
12307 self.show_cursor_when_unfocused = is_enabled;
12308 cx.notify();
12309 }
12310
12311 pub fn lsp_store(&self, cx: &AppContext) -> Option<Model<LspStore>> {
12312 self.project
12313 .as_ref()
12314 .map(|project| project.read(cx).lsp_store())
12315 }
12316
12317 fn on_buffer_changed(&mut self, _: Model<MultiBuffer>, cx: &mut ViewContext<Self>) {
12318 cx.notify();
12319 }
12320
12321 fn on_buffer_event(
12322 &mut self,
12323 multibuffer: Model<MultiBuffer>,
12324 event: &multi_buffer::Event,
12325 cx: &mut ViewContext<Self>,
12326 ) {
12327 match event {
12328 multi_buffer::Event::Edited {
12329 singleton_buffer_edited,
12330 edited_buffer: buffer_edited,
12331 } => {
12332 self.scrollbar_marker_state.dirty = true;
12333 self.active_indent_guides_state.dirty = true;
12334 self.refresh_active_diagnostics(cx);
12335 self.refresh_code_actions(cx);
12336 if self.has_active_inline_completion() {
12337 self.update_visible_inline_completion(cx);
12338 }
12339 if let Some(buffer) = buffer_edited {
12340 let buffer_id = buffer.read(cx).remote_id();
12341 if !self.registered_buffers.contains_key(&buffer_id) {
12342 if let Some(lsp_store) = self.lsp_store(cx) {
12343 lsp_store.update(cx, |lsp_store, cx| {
12344 self.registered_buffers.insert(
12345 buffer_id,
12346 lsp_store.register_buffer_with_language_servers(&buffer, cx),
12347 );
12348 })
12349 }
12350 }
12351 }
12352 cx.emit(EditorEvent::BufferEdited);
12353 cx.emit(SearchEvent::MatchesInvalidated);
12354 if *singleton_buffer_edited {
12355 if let Some(project) = &self.project {
12356 let project = project.read(cx);
12357 #[allow(clippy::mutable_key_type)]
12358 let languages_affected = multibuffer
12359 .read(cx)
12360 .all_buffers()
12361 .into_iter()
12362 .filter_map(|buffer| {
12363 let buffer = buffer.read(cx);
12364 let language = buffer.language()?;
12365 if project.is_local()
12366 && project
12367 .language_servers_for_local_buffer(buffer, cx)
12368 .count()
12369 == 0
12370 {
12371 None
12372 } else {
12373 Some(language)
12374 }
12375 })
12376 .cloned()
12377 .collect::<HashSet<_>>();
12378 if !languages_affected.is_empty() {
12379 self.refresh_inlay_hints(
12380 InlayHintRefreshReason::BufferEdited(languages_affected),
12381 cx,
12382 );
12383 }
12384 }
12385 }
12386
12387 let Some(project) = &self.project else { return };
12388 let (telemetry, is_via_ssh) = {
12389 let project = project.read(cx);
12390 let telemetry = project.client().telemetry().clone();
12391 let is_via_ssh = project.is_via_ssh();
12392 (telemetry, is_via_ssh)
12393 };
12394 refresh_linked_ranges(self, cx);
12395 telemetry.log_edit_event("editor", is_via_ssh);
12396 }
12397 multi_buffer::Event::ExcerptsAdded {
12398 buffer,
12399 predecessor,
12400 excerpts,
12401 } => {
12402 self.tasks_update_task = Some(self.refresh_runnables(cx));
12403 let buffer_id = buffer.read(cx).remote_id();
12404 if !self.diff_map.diff_bases.contains_key(&buffer_id) {
12405 if let Some(project) = &self.project {
12406 get_unstaged_changes_for_buffers(project, [buffer.clone()], cx);
12407 }
12408 }
12409 cx.emit(EditorEvent::ExcerptsAdded {
12410 buffer: buffer.clone(),
12411 predecessor: *predecessor,
12412 excerpts: excerpts.clone(),
12413 });
12414 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
12415 }
12416 multi_buffer::Event::ExcerptsRemoved { ids } => {
12417 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
12418 let buffer = self.buffer.read(cx);
12419 self.registered_buffers
12420 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
12421 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
12422 }
12423 multi_buffer::Event::ExcerptsEdited { ids } => {
12424 cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
12425 }
12426 multi_buffer::Event::ExcerptsExpanded { ids } => {
12427 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
12428 }
12429 multi_buffer::Event::Reparsed(buffer_id) => {
12430 self.tasks_update_task = Some(self.refresh_runnables(cx));
12431
12432 cx.emit(EditorEvent::Reparsed(*buffer_id));
12433 }
12434 multi_buffer::Event::LanguageChanged(buffer_id) => {
12435 linked_editing_ranges::refresh_linked_ranges(self, cx);
12436 cx.emit(EditorEvent::Reparsed(*buffer_id));
12437 cx.notify();
12438 }
12439 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
12440 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
12441 multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
12442 cx.emit(EditorEvent::TitleChanged)
12443 }
12444 // multi_buffer::Event::DiffBaseChanged => {
12445 // self.scrollbar_marker_state.dirty = true;
12446 // cx.emit(EditorEvent::DiffBaseChanged);
12447 // cx.notify();
12448 // }
12449 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
12450 multi_buffer::Event::DiagnosticsUpdated => {
12451 self.refresh_active_diagnostics(cx);
12452 self.scrollbar_marker_state.dirty = true;
12453 cx.notify();
12454 }
12455 _ => {}
12456 };
12457 }
12458
12459 fn on_display_map_changed(&mut self, _: Model<DisplayMap>, cx: &mut ViewContext<Self>) {
12460 cx.notify();
12461 }
12462
12463 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
12464 self.tasks_update_task = Some(self.refresh_runnables(cx));
12465 self.refresh_inline_completion(true, false, cx);
12466 self.refresh_inlay_hints(
12467 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
12468 self.selections.newest_anchor().head(),
12469 &self.buffer.read(cx).snapshot(cx),
12470 cx,
12471 )),
12472 cx,
12473 );
12474
12475 let old_cursor_shape = self.cursor_shape;
12476
12477 {
12478 let editor_settings = EditorSettings::get_global(cx);
12479 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
12480 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
12481 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
12482 }
12483
12484 if old_cursor_shape != self.cursor_shape {
12485 cx.emit(EditorEvent::CursorShapeChanged);
12486 }
12487
12488 let project_settings = ProjectSettings::get_global(cx);
12489 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
12490
12491 if self.mode == EditorMode::Full {
12492 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
12493 if self.git_blame_inline_enabled != inline_blame_enabled {
12494 self.toggle_git_blame_inline_internal(false, cx);
12495 }
12496 }
12497
12498 cx.notify();
12499 }
12500
12501 pub fn set_searchable(&mut self, searchable: bool) {
12502 self.searchable = searchable;
12503 }
12504
12505 pub fn searchable(&self) -> bool {
12506 self.searchable
12507 }
12508
12509 fn open_proposed_changes_editor(
12510 &mut self,
12511 _: &OpenProposedChangesEditor,
12512 cx: &mut ViewContext<Self>,
12513 ) {
12514 let Some(workspace) = self.workspace() else {
12515 cx.propagate();
12516 return;
12517 };
12518
12519 let selections = self.selections.all::<usize>(cx);
12520 let multi_buffer = self.buffer.read(cx);
12521 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12522 let mut new_selections_by_buffer = HashMap::default();
12523 for selection in selections {
12524 for (excerpt, range) in
12525 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
12526 {
12527 let mut range = range.to_point(excerpt.buffer());
12528 range.start.column = 0;
12529 range.end.column = excerpt.buffer().line_len(range.end.row);
12530 new_selections_by_buffer
12531 .entry(multi_buffer.buffer(excerpt.buffer_id()).unwrap())
12532 .or_insert(Vec::new())
12533 .push(range)
12534 }
12535 }
12536
12537 let proposed_changes_buffers = new_selections_by_buffer
12538 .into_iter()
12539 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
12540 .collect::<Vec<_>>();
12541 let proposed_changes_editor = cx.new_view(|cx| {
12542 ProposedChangesEditor::new(
12543 "Proposed changes",
12544 proposed_changes_buffers,
12545 self.project.clone(),
12546 cx,
12547 )
12548 });
12549
12550 cx.window_context().defer(move |cx| {
12551 workspace.update(cx, |workspace, cx| {
12552 workspace.active_pane().update(cx, |pane, cx| {
12553 pane.add_item(Box::new(proposed_changes_editor), true, true, None, cx);
12554 });
12555 });
12556 });
12557 }
12558
12559 pub fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
12560 self.open_excerpts_common(None, true, cx)
12561 }
12562
12563 pub fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
12564 self.open_excerpts_common(None, false, cx)
12565 }
12566
12567 fn open_excerpts_common(
12568 &mut self,
12569 jump_data: Option<JumpData>,
12570 split: bool,
12571 cx: &mut ViewContext<Self>,
12572 ) {
12573 let Some(workspace) = self.workspace() else {
12574 cx.propagate();
12575 return;
12576 };
12577
12578 if self.buffer.read(cx).is_singleton() {
12579 cx.propagate();
12580 return;
12581 }
12582
12583 let mut new_selections_by_buffer = HashMap::default();
12584 match &jump_data {
12585 Some(JumpData::MultiBufferPoint {
12586 excerpt_id,
12587 position,
12588 anchor,
12589 line_offset_from_top,
12590 }) => {
12591 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
12592 if let Some(buffer) = multi_buffer_snapshot
12593 .buffer_id_for_excerpt(*excerpt_id)
12594 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
12595 {
12596 let buffer_snapshot = buffer.read(cx).snapshot();
12597 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
12598 language::ToPoint::to_point(anchor, &buffer_snapshot)
12599 } else {
12600 buffer_snapshot.clip_point(*position, Bias::Left)
12601 };
12602 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
12603 new_selections_by_buffer.insert(
12604 buffer,
12605 (
12606 vec![jump_to_offset..jump_to_offset],
12607 Some(*line_offset_from_top),
12608 ),
12609 );
12610 }
12611 }
12612 Some(JumpData::MultiBufferRow {
12613 row,
12614 line_offset_from_top,
12615 }) => {
12616 let point = MultiBufferPoint::new(row.0, 0);
12617 if let Some((buffer, buffer_point, _)) =
12618 self.buffer.read(cx).point_to_buffer_point(point, cx)
12619 {
12620 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
12621 new_selections_by_buffer
12622 .entry(buffer)
12623 .or_insert((Vec::new(), Some(*line_offset_from_top)))
12624 .0
12625 .push(buffer_offset..buffer_offset)
12626 }
12627 }
12628 None => {
12629 let selections = self.selections.all::<usize>(cx);
12630 let multi_buffer = self.buffer.read(cx);
12631 for selection in selections {
12632 for (excerpt, mut range) in multi_buffer
12633 .snapshot(cx)
12634 .range_to_buffer_ranges(selection.range())
12635 {
12636 // When editing branch buffers, jump to the corresponding location
12637 // in their base buffer.
12638 let mut buffer_handle = multi_buffer.buffer(excerpt.buffer_id()).unwrap();
12639 let buffer = buffer_handle.read(cx);
12640 if let Some(base_buffer) = buffer.base_buffer() {
12641 range = buffer.range_to_version(range, &base_buffer.read(cx).version());
12642 buffer_handle = base_buffer;
12643 }
12644
12645 if selection.reversed {
12646 mem::swap(&mut range.start, &mut range.end);
12647 }
12648 new_selections_by_buffer
12649 .entry(buffer_handle)
12650 .or_insert((Vec::new(), None))
12651 .0
12652 .push(range)
12653 }
12654 }
12655 }
12656 }
12657
12658 if new_selections_by_buffer.is_empty() {
12659 return;
12660 }
12661
12662 // We defer the pane interaction because we ourselves are a workspace item
12663 // and activating a new item causes the pane to call a method on us reentrantly,
12664 // which panics if we're on the stack.
12665 cx.window_context().defer(move |cx| {
12666 workspace.update(cx, |workspace, cx| {
12667 let pane = if split {
12668 workspace.adjacent_pane(cx)
12669 } else {
12670 workspace.active_pane().clone()
12671 };
12672
12673 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
12674 let editor = buffer
12675 .read(cx)
12676 .file()
12677 .is_none()
12678 .then(|| {
12679 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
12680 // so `workspace.open_project_item` will never find them, always opening a new editor.
12681 // Instead, we try to activate the existing editor in the pane first.
12682 let (editor, pane_item_index) =
12683 pane.read(cx).items().enumerate().find_map(|(i, item)| {
12684 let editor = item.downcast::<Editor>()?;
12685 let singleton_buffer =
12686 editor.read(cx).buffer().read(cx).as_singleton()?;
12687 if singleton_buffer == buffer {
12688 Some((editor, i))
12689 } else {
12690 None
12691 }
12692 })?;
12693 pane.update(cx, |pane, cx| {
12694 pane.activate_item(pane_item_index, true, true, cx)
12695 });
12696 Some(editor)
12697 })
12698 .flatten()
12699 .unwrap_or_else(|| {
12700 workspace.open_project_item::<Self>(
12701 pane.clone(),
12702 buffer,
12703 true,
12704 true,
12705 cx,
12706 )
12707 });
12708
12709 editor.update(cx, |editor, cx| {
12710 let autoscroll = match scroll_offset {
12711 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
12712 None => Autoscroll::newest(),
12713 };
12714 let nav_history = editor.nav_history.take();
12715 editor.change_selections(Some(autoscroll), cx, |s| {
12716 s.select_ranges(ranges);
12717 });
12718 editor.nav_history = nav_history;
12719 });
12720 }
12721 })
12722 });
12723 }
12724
12725 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
12726 let snapshot = self.buffer.read(cx).read(cx);
12727 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
12728 Some(
12729 ranges
12730 .iter()
12731 .map(move |range| {
12732 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
12733 })
12734 .collect(),
12735 )
12736 }
12737
12738 fn selection_replacement_ranges(
12739 &self,
12740 range: Range<OffsetUtf16>,
12741 cx: &mut AppContext,
12742 ) -> Vec<Range<OffsetUtf16>> {
12743 let selections = self.selections.all::<OffsetUtf16>(cx);
12744 let newest_selection = selections
12745 .iter()
12746 .max_by_key(|selection| selection.id)
12747 .unwrap();
12748 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
12749 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
12750 let snapshot = self.buffer.read(cx).read(cx);
12751 selections
12752 .into_iter()
12753 .map(|mut selection| {
12754 selection.start.0 =
12755 (selection.start.0 as isize).saturating_add(start_delta) as usize;
12756 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
12757 snapshot.clip_offset_utf16(selection.start, Bias::Left)
12758 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
12759 })
12760 .collect()
12761 }
12762
12763 fn report_editor_event(
12764 &self,
12765 event_type: &'static str,
12766 file_extension: Option<String>,
12767 cx: &AppContext,
12768 ) {
12769 if cfg!(any(test, feature = "test-support")) {
12770 return;
12771 }
12772
12773 let Some(project) = &self.project else { return };
12774
12775 // If None, we are in a file without an extension
12776 let file = self
12777 .buffer
12778 .read(cx)
12779 .as_singleton()
12780 .and_then(|b| b.read(cx).file());
12781 let file_extension = file_extension.or(file
12782 .as_ref()
12783 .and_then(|file| Path::new(file.file_name(cx)).extension())
12784 .and_then(|e| e.to_str())
12785 .map(|a| a.to_string()));
12786
12787 let vim_mode = cx
12788 .global::<SettingsStore>()
12789 .raw_user_settings()
12790 .get("vim_mode")
12791 == Some(&serde_json::Value::Bool(true));
12792
12793 let copilot_enabled = all_language_settings(file, cx).inline_completions.provider
12794 == language::language_settings::InlineCompletionProvider::Copilot;
12795 let copilot_enabled_for_language = self
12796 .buffer
12797 .read(cx)
12798 .settings_at(0, cx)
12799 .show_inline_completions;
12800
12801 let project = project.read(cx);
12802 telemetry::event!(
12803 event_type,
12804 file_extension,
12805 vim_mode,
12806 copilot_enabled,
12807 copilot_enabled_for_language,
12808 is_via_ssh = project.is_via_ssh(),
12809 );
12810 }
12811
12812 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
12813 /// with each line being an array of {text, highlight} objects.
12814 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
12815 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
12816 return;
12817 };
12818
12819 #[derive(Serialize)]
12820 struct Chunk<'a> {
12821 text: String,
12822 highlight: Option<&'a str>,
12823 }
12824
12825 let snapshot = buffer.read(cx).snapshot();
12826 let range = self
12827 .selected_text_range(false, cx)
12828 .and_then(|selection| {
12829 if selection.range.is_empty() {
12830 None
12831 } else {
12832 Some(selection.range)
12833 }
12834 })
12835 .unwrap_or_else(|| 0..snapshot.len());
12836
12837 let chunks = snapshot.chunks(range, true);
12838 let mut lines = Vec::new();
12839 let mut line: VecDeque<Chunk> = VecDeque::new();
12840
12841 let Some(style) = self.style.as_ref() else {
12842 return;
12843 };
12844
12845 for chunk in chunks {
12846 let highlight = chunk
12847 .syntax_highlight_id
12848 .and_then(|id| id.name(&style.syntax));
12849 let mut chunk_lines = chunk.text.split('\n').peekable();
12850 while let Some(text) = chunk_lines.next() {
12851 let mut merged_with_last_token = false;
12852 if let Some(last_token) = line.back_mut() {
12853 if last_token.highlight == highlight {
12854 last_token.text.push_str(text);
12855 merged_with_last_token = true;
12856 }
12857 }
12858
12859 if !merged_with_last_token {
12860 line.push_back(Chunk {
12861 text: text.into(),
12862 highlight,
12863 });
12864 }
12865
12866 if chunk_lines.peek().is_some() {
12867 if line.len() > 1 && line.front().unwrap().text.is_empty() {
12868 line.pop_front();
12869 }
12870 if line.len() > 1 && line.back().unwrap().text.is_empty() {
12871 line.pop_back();
12872 }
12873
12874 lines.push(mem::take(&mut line));
12875 }
12876 }
12877 }
12878
12879 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
12880 return;
12881 };
12882 cx.write_to_clipboard(ClipboardItem::new_string(lines));
12883 }
12884
12885 pub fn open_context_menu(&mut self, _: &OpenContextMenu, cx: &mut ViewContext<Self>) {
12886 self.request_autoscroll(Autoscroll::newest(), cx);
12887 let position = self.selections.newest_display(cx).start;
12888 mouse_context_menu::deploy_context_menu(self, None, position, cx);
12889 }
12890
12891 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
12892 &self.inlay_hint_cache
12893 }
12894
12895 pub fn replay_insert_event(
12896 &mut self,
12897 text: &str,
12898 relative_utf16_range: Option<Range<isize>>,
12899 cx: &mut ViewContext<Self>,
12900 ) {
12901 if !self.input_enabled {
12902 cx.emit(EditorEvent::InputIgnored { text: text.into() });
12903 return;
12904 }
12905 if let Some(relative_utf16_range) = relative_utf16_range {
12906 let selections = self.selections.all::<OffsetUtf16>(cx);
12907 self.change_selections(None, cx, |s| {
12908 let new_ranges = selections.into_iter().map(|range| {
12909 let start = OffsetUtf16(
12910 range
12911 .head()
12912 .0
12913 .saturating_add_signed(relative_utf16_range.start),
12914 );
12915 let end = OffsetUtf16(
12916 range
12917 .head()
12918 .0
12919 .saturating_add_signed(relative_utf16_range.end),
12920 );
12921 start..end
12922 });
12923 s.select_ranges(new_ranges);
12924 });
12925 }
12926
12927 self.handle_input(text, cx);
12928 }
12929
12930 pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
12931 let Some(provider) = self.semantics_provider.as_ref() else {
12932 return false;
12933 };
12934
12935 let mut supports = false;
12936 self.buffer().read(cx).for_each_buffer(|buffer| {
12937 supports |= provider.supports_inlay_hints(buffer, cx);
12938 });
12939 supports
12940 }
12941
12942 pub fn focus(&self, cx: &mut WindowContext) {
12943 cx.focus(&self.focus_handle)
12944 }
12945
12946 pub fn is_focused(&self, cx: &WindowContext) -> bool {
12947 self.focus_handle.is_focused(cx)
12948 }
12949
12950 fn handle_focus(&mut self, cx: &mut ViewContext<Self>) {
12951 cx.emit(EditorEvent::Focused);
12952
12953 if let Some(descendant) = self
12954 .last_focused_descendant
12955 .take()
12956 .and_then(|descendant| descendant.upgrade())
12957 {
12958 cx.focus(&descendant);
12959 } else {
12960 if let Some(blame) = self.blame.as_ref() {
12961 blame.update(cx, GitBlame::focus)
12962 }
12963
12964 self.blink_manager.update(cx, BlinkManager::enable);
12965 self.show_cursor_names(cx);
12966 self.buffer.update(cx, |buffer, cx| {
12967 buffer.finalize_last_transaction(cx);
12968 if self.leader_peer_id.is_none() {
12969 buffer.set_active_selections(
12970 &self.selections.disjoint_anchors(),
12971 self.selections.line_mode,
12972 self.cursor_shape,
12973 cx,
12974 );
12975 }
12976 });
12977 }
12978 }
12979
12980 fn handle_focus_in(&mut self, cx: &mut ViewContext<Self>) {
12981 cx.emit(EditorEvent::FocusedIn)
12982 }
12983
12984 fn handle_focus_out(&mut self, event: FocusOutEvent, _cx: &mut ViewContext<Self>) {
12985 if event.blurred != self.focus_handle {
12986 self.last_focused_descendant = Some(event.blurred);
12987 }
12988 }
12989
12990 pub fn handle_blur(&mut self, cx: &mut ViewContext<Self>) {
12991 self.blink_manager.update(cx, BlinkManager::disable);
12992 self.buffer
12993 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
12994
12995 if let Some(blame) = self.blame.as_ref() {
12996 blame.update(cx, GitBlame::blur)
12997 }
12998 if !self.hover_state.focused(cx) {
12999 hide_hover(self, cx);
13000 }
13001
13002 self.hide_context_menu(cx);
13003 cx.emit(EditorEvent::Blurred);
13004 cx.notify();
13005 }
13006
13007 pub fn register_action<A: Action>(
13008 &mut self,
13009 listener: impl Fn(&A, &mut WindowContext) + 'static,
13010 ) -> Subscription {
13011 let id = self.next_editor_action_id.post_inc();
13012 let listener = Arc::new(listener);
13013 self.editor_actions.borrow_mut().insert(
13014 id,
13015 Box::new(move |cx| {
13016 let cx = cx.window_context();
13017 let listener = listener.clone();
13018 cx.on_action(TypeId::of::<A>(), move |action, phase, cx| {
13019 let action = action.downcast_ref().unwrap();
13020 if phase == DispatchPhase::Bubble {
13021 listener(action, cx)
13022 }
13023 })
13024 }),
13025 );
13026
13027 let editor_actions = self.editor_actions.clone();
13028 Subscription::new(move || {
13029 editor_actions.borrow_mut().remove(&id);
13030 })
13031 }
13032
13033 pub fn file_header_size(&self) -> u32 {
13034 FILE_HEADER_HEIGHT
13035 }
13036
13037 pub fn revert(
13038 &mut self,
13039 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
13040 cx: &mut ViewContext<Self>,
13041 ) {
13042 self.buffer().update(cx, |multi_buffer, cx| {
13043 for (buffer_id, changes) in revert_changes {
13044 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
13045 buffer.update(cx, |buffer, cx| {
13046 buffer.edit(
13047 changes.into_iter().map(|(range, text)| {
13048 (range, text.to_string().map(Arc::<str>::from))
13049 }),
13050 None,
13051 cx,
13052 );
13053 });
13054 }
13055 }
13056 });
13057 self.change_selections(None, cx, |selections| selections.refresh());
13058 }
13059
13060 pub fn to_pixel_point(
13061 &mut self,
13062 source: multi_buffer::Anchor,
13063 editor_snapshot: &EditorSnapshot,
13064 cx: &mut ViewContext<Self>,
13065 ) -> Option<gpui::Point<Pixels>> {
13066 let source_point = source.to_display_point(editor_snapshot);
13067 self.display_to_pixel_point(source_point, editor_snapshot, cx)
13068 }
13069
13070 pub fn display_to_pixel_point(
13071 &self,
13072 source: DisplayPoint,
13073 editor_snapshot: &EditorSnapshot,
13074 cx: &WindowContext,
13075 ) -> Option<gpui::Point<Pixels>> {
13076 let line_height = self.style()?.text.line_height_in_pixels(cx.rem_size());
13077 let text_layout_details = self.text_layout_details(cx);
13078 let scroll_top = text_layout_details
13079 .scroll_anchor
13080 .scroll_position(editor_snapshot)
13081 .y;
13082
13083 if source.row().as_f32() < scroll_top.floor() {
13084 return None;
13085 }
13086 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
13087 let source_y = line_height * (source.row().as_f32() - scroll_top);
13088 Some(gpui::Point::new(source_x, source_y))
13089 }
13090
13091 pub fn has_active_completions_menu(&self) -> bool {
13092 self.context_menu.borrow().as_ref().map_or(false, |menu| {
13093 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
13094 })
13095 }
13096
13097 pub fn register_addon<T: Addon>(&mut self, instance: T) {
13098 self.addons
13099 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
13100 }
13101
13102 pub fn unregister_addon<T: Addon>(&mut self) {
13103 self.addons.remove(&std::any::TypeId::of::<T>());
13104 }
13105
13106 pub fn addon<T: Addon>(&self) -> Option<&T> {
13107 let type_id = std::any::TypeId::of::<T>();
13108 self.addons
13109 .get(&type_id)
13110 .and_then(|item| item.to_any().downcast_ref::<T>())
13111 }
13112
13113 pub fn add_change_set(
13114 &mut self,
13115 change_set: Model<BufferChangeSet>,
13116 cx: &mut ViewContext<Self>,
13117 ) {
13118 self.diff_map.add_change_set(change_set, cx);
13119 }
13120
13121 fn character_size(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
13122 let text_layout_details = self.text_layout_details(cx);
13123 let style = &text_layout_details.editor_style;
13124 let font_id = cx.text_system().resolve_font(&style.text.font());
13125 let font_size = style.text.font_size.to_pixels(cx.rem_size());
13126 let line_height = style.text.line_height_in_pixels(cx.rem_size());
13127
13128 let em_width = cx
13129 .text_system()
13130 .typographic_bounds(font_id, font_size, 'm')
13131 .unwrap()
13132 .size
13133 .width;
13134
13135 gpui::Point::new(em_width, line_height)
13136 }
13137}
13138
13139fn get_unstaged_changes_for_buffers(
13140 project: &Model<Project>,
13141 buffers: impl IntoIterator<Item = Model<Buffer>>,
13142 cx: &mut ViewContext<Editor>,
13143) {
13144 let mut tasks = Vec::new();
13145 project.update(cx, |project, cx| {
13146 for buffer in buffers {
13147 tasks.push(project.open_unstaged_changes(buffer.clone(), cx))
13148 }
13149 });
13150 cx.spawn(|this, mut cx| async move {
13151 let change_sets = futures::future::join_all(tasks).await;
13152 this.update(&mut cx, |this, cx| {
13153 for change_set in change_sets {
13154 if let Some(change_set) = change_set.log_err() {
13155 this.diff_map.add_change_set(change_set, cx);
13156 }
13157 }
13158 })
13159 .ok();
13160 })
13161 .detach();
13162}
13163
13164fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
13165 let tab_size = tab_size.get() as usize;
13166 let mut width = offset;
13167
13168 for ch in text.chars() {
13169 width += if ch == '\t' {
13170 tab_size - (width % tab_size)
13171 } else {
13172 1
13173 };
13174 }
13175
13176 width - offset
13177}
13178
13179#[cfg(test)]
13180mod tests {
13181 use super::*;
13182
13183 #[test]
13184 fn test_string_size_with_expanded_tabs() {
13185 let nz = |val| NonZeroU32::new(val).unwrap();
13186 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
13187 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
13188 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
13189 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
13190 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
13191 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
13192 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
13193 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
13194 }
13195}
13196
13197/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
13198struct WordBreakingTokenizer<'a> {
13199 input: &'a str,
13200}
13201
13202impl<'a> WordBreakingTokenizer<'a> {
13203 fn new(input: &'a str) -> Self {
13204 Self { input }
13205 }
13206}
13207
13208fn is_char_ideographic(ch: char) -> bool {
13209 use unicode_script::Script::*;
13210 use unicode_script::UnicodeScript;
13211 matches!(ch.script(), Han | Tangut | Yi)
13212}
13213
13214fn is_grapheme_ideographic(text: &str) -> bool {
13215 text.chars().any(is_char_ideographic)
13216}
13217
13218fn is_grapheme_whitespace(text: &str) -> bool {
13219 text.chars().any(|x| x.is_whitespace())
13220}
13221
13222fn should_stay_with_preceding_ideograph(text: &str) -> bool {
13223 text.chars().next().map_or(false, |ch| {
13224 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
13225 })
13226}
13227
13228#[derive(PartialEq, Eq, Debug, Clone, Copy)]
13229struct WordBreakToken<'a> {
13230 token: &'a str,
13231 grapheme_len: usize,
13232 is_whitespace: bool,
13233}
13234
13235impl<'a> Iterator for WordBreakingTokenizer<'a> {
13236 /// Yields a span, the count of graphemes in the token, and whether it was
13237 /// whitespace. Note that it also breaks at word boundaries.
13238 type Item = WordBreakToken<'a>;
13239
13240 fn next(&mut self) -> Option<Self::Item> {
13241 use unicode_segmentation::UnicodeSegmentation;
13242 if self.input.is_empty() {
13243 return None;
13244 }
13245
13246 let mut iter = self.input.graphemes(true).peekable();
13247 let mut offset = 0;
13248 let mut graphemes = 0;
13249 if let Some(first_grapheme) = iter.next() {
13250 let is_whitespace = is_grapheme_whitespace(first_grapheme);
13251 offset += first_grapheme.len();
13252 graphemes += 1;
13253 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
13254 if let Some(grapheme) = iter.peek().copied() {
13255 if should_stay_with_preceding_ideograph(grapheme) {
13256 offset += grapheme.len();
13257 graphemes += 1;
13258 }
13259 }
13260 } else {
13261 let mut words = self.input[offset..].split_word_bound_indices().peekable();
13262 let mut next_word_bound = words.peek().copied();
13263 if next_word_bound.map_or(false, |(i, _)| i == 0) {
13264 next_word_bound = words.next();
13265 }
13266 while let Some(grapheme) = iter.peek().copied() {
13267 if next_word_bound.map_or(false, |(i, _)| i == offset) {
13268 break;
13269 };
13270 if is_grapheme_whitespace(grapheme) != is_whitespace {
13271 break;
13272 };
13273 offset += grapheme.len();
13274 graphemes += 1;
13275 iter.next();
13276 }
13277 }
13278 let token = &self.input[..offset];
13279 self.input = &self.input[offset..];
13280 if is_whitespace {
13281 Some(WordBreakToken {
13282 token: " ",
13283 grapheme_len: 1,
13284 is_whitespace: true,
13285 })
13286 } else {
13287 Some(WordBreakToken {
13288 token,
13289 grapheme_len: graphemes,
13290 is_whitespace: false,
13291 })
13292 }
13293 } else {
13294 None
13295 }
13296 }
13297}
13298
13299#[test]
13300fn test_word_breaking_tokenizer() {
13301 let tests: &[(&str, &[(&str, usize, bool)])] = &[
13302 ("", &[]),
13303 (" ", &[(" ", 1, true)]),
13304 ("Ʒ", &[("Ʒ", 1, false)]),
13305 ("Ǽ", &[("Ǽ", 1, false)]),
13306 ("⋑", &[("⋑", 1, false)]),
13307 ("⋑⋑", &[("⋑⋑", 2, false)]),
13308 (
13309 "原理,进而",
13310 &[
13311 ("原", 1, false),
13312 ("理,", 2, false),
13313 ("进", 1, false),
13314 ("而", 1, false),
13315 ],
13316 ),
13317 (
13318 "hello world",
13319 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
13320 ),
13321 (
13322 "hello, world",
13323 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
13324 ),
13325 (
13326 " hello world",
13327 &[
13328 (" ", 1, true),
13329 ("hello", 5, false),
13330 (" ", 1, true),
13331 ("world", 5, false),
13332 ],
13333 ),
13334 (
13335 "这是什么 \n 钢笔",
13336 &[
13337 ("这", 1, false),
13338 ("是", 1, false),
13339 ("什", 1, false),
13340 ("么", 1, false),
13341 (" ", 1, true),
13342 ("钢", 1, false),
13343 ("笔", 1, false),
13344 ],
13345 ),
13346 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
13347 ];
13348
13349 for (input, result) in tests {
13350 assert_eq!(
13351 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
13352 result
13353 .iter()
13354 .copied()
13355 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
13356 token,
13357 grapheme_len,
13358 is_whitespace,
13359 })
13360 .collect::<Vec<_>>()
13361 );
13362 }
13363}
13364
13365fn wrap_with_prefix(
13366 line_prefix: String,
13367 unwrapped_text: String,
13368 wrap_column: usize,
13369 tab_size: NonZeroU32,
13370) -> String {
13371 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
13372 let mut wrapped_text = String::new();
13373 let mut current_line = line_prefix.clone();
13374
13375 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
13376 let mut current_line_len = line_prefix_len;
13377 for WordBreakToken {
13378 token,
13379 grapheme_len,
13380 is_whitespace,
13381 } in tokenizer
13382 {
13383 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
13384 wrapped_text.push_str(current_line.trim_end());
13385 wrapped_text.push('\n');
13386 current_line.truncate(line_prefix.len());
13387 current_line_len = line_prefix_len;
13388 if !is_whitespace {
13389 current_line.push_str(token);
13390 current_line_len += grapheme_len;
13391 }
13392 } else if !is_whitespace {
13393 current_line.push_str(token);
13394 current_line_len += grapheme_len;
13395 } else if current_line_len != line_prefix_len {
13396 current_line.push(' ');
13397 current_line_len += 1;
13398 }
13399 }
13400
13401 if !current_line.is_empty() {
13402 wrapped_text.push_str(¤t_line);
13403 }
13404 wrapped_text
13405}
13406
13407#[test]
13408fn test_wrap_with_prefix() {
13409 assert_eq!(
13410 wrap_with_prefix(
13411 "# ".to_string(),
13412 "abcdefg".to_string(),
13413 4,
13414 NonZeroU32::new(4).unwrap()
13415 ),
13416 "# abcdefg"
13417 );
13418 assert_eq!(
13419 wrap_with_prefix(
13420 "".to_string(),
13421 "\thello world".to_string(),
13422 8,
13423 NonZeroU32::new(4).unwrap()
13424 ),
13425 "hello\nworld"
13426 );
13427 assert_eq!(
13428 wrap_with_prefix(
13429 "// ".to_string(),
13430 "xx \nyy zz aa bb cc".to_string(),
13431 12,
13432 NonZeroU32::new(4).unwrap()
13433 ),
13434 "// xx yy zz\n// aa bb cc"
13435 );
13436 assert_eq!(
13437 wrap_with_prefix(
13438 String::new(),
13439 "这是什么 \n 钢笔".to_string(),
13440 3,
13441 NonZeroU32::new(4).unwrap()
13442 ),
13443 "这是什\n么 钢\n笔"
13444 );
13445}
13446
13447fn hunks_for_selections(
13448 snapshot: &EditorSnapshot,
13449 selections: &[Selection<Point>],
13450) -> Vec<MultiBufferDiffHunk> {
13451 hunks_for_ranges(
13452 selections.iter().map(|selection| selection.range()),
13453 snapshot,
13454 )
13455}
13456
13457pub fn hunks_for_ranges(
13458 ranges: impl Iterator<Item = Range<Point>>,
13459 snapshot: &EditorSnapshot,
13460) -> Vec<MultiBufferDiffHunk> {
13461 let mut hunks = Vec::new();
13462 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
13463 HashMap::default();
13464 for query_range in ranges {
13465 let query_rows =
13466 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
13467 for hunk in snapshot.diff_map.diff_hunks_in_range(
13468 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
13469 &snapshot.buffer_snapshot,
13470 ) {
13471 // Deleted hunk is an empty row range, no caret can be placed there and Zed allows to revert it
13472 // when the caret is just above or just below the deleted hunk.
13473 let allow_adjacent = hunk_status(&hunk) == DiffHunkStatus::Removed;
13474 let related_to_selection = if allow_adjacent {
13475 hunk.row_range.overlaps(&query_rows)
13476 || hunk.row_range.start == query_rows.end
13477 || hunk.row_range.end == query_rows.start
13478 } else {
13479 hunk.row_range.overlaps(&query_rows)
13480 };
13481 if related_to_selection {
13482 if !processed_buffer_rows
13483 .entry(hunk.buffer_id)
13484 .or_default()
13485 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
13486 {
13487 continue;
13488 }
13489 hunks.push(hunk);
13490 }
13491 }
13492 }
13493
13494 hunks
13495}
13496
13497pub trait CollaborationHub {
13498 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
13499 fn user_participant_indices<'a>(
13500 &self,
13501 cx: &'a AppContext,
13502 ) -> &'a HashMap<u64, ParticipantIndex>;
13503 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString>;
13504}
13505
13506impl CollaborationHub for Model<Project> {
13507 fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
13508 self.read(cx).collaborators()
13509 }
13510
13511 fn user_participant_indices<'a>(
13512 &self,
13513 cx: &'a AppContext,
13514 ) -> &'a HashMap<u64, ParticipantIndex> {
13515 self.read(cx).user_store().read(cx).participant_indices()
13516 }
13517
13518 fn user_names(&self, cx: &AppContext) -> HashMap<u64, SharedString> {
13519 let this = self.read(cx);
13520 let user_ids = this.collaborators().values().map(|c| c.user_id);
13521 this.user_store().read_with(cx, |user_store, cx| {
13522 user_store.participant_names(user_ids, cx)
13523 })
13524 }
13525}
13526
13527pub trait SemanticsProvider {
13528 fn hover(
13529 &self,
13530 buffer: &Model<Buffer>,
13531 position: text::Anchor,
13532 cx: &mut AppContext,
13533 ) -> Option<Task<Vec<project::Hover>>>;
13534
13535 fn inlay_hints(
13536 &self,
13537 buffer_handle: Model<Buffer>,
13538 range: Range<text::Anchor>,
13539 cx: &mut AppContext,
13540 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
13541
13542 fn resolve_inlay_hint(
13543 &self,
13544 hint: InlayHint,
13545 buffer_handle: Model<Buffer>,
13546 server_id: LanguageServerId,
13547 cx: &mut AppContext,
13548 ) -> Option<Task<anyhow::Result<InlayHint>>>;
13549
13550 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
13551
13552 fn document_highlights(
13553 &self,
13554 buffer: &Model<Buffer>,
13555 position: text::Anchor,
13556 cx: &mut AppContext,
13557 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
13558
13559 fn definitions(
13560 &self,
13561 buffer: &Model<Buffer>,
13562 position: text::Anchor,
13563 kind: GotoDefinitionKind,
13564 cx: &mut AppContext,
13565 ) -> Option<Task<Result<Vec<LocationLink>>>>;
13566
13567 fn range_for_rename(
13568 &self,
13569 buffer: &Model<Buffer>,
13570 position: text::Anchor,
13571 cx: &mut AppContext,
13572 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
13573
13574 fn perform_rename(
13575 &self,
13576 buffer: &Model<Buffer>,
13577 position: text::Anchor,
13578 new_name: String,
13579 cx: &mut AppContext,
13580 ) -> Option<Task<Result<ProjectTransaction>>>;
13581}
13582
13583pub trait CompletionProvider {
13584 fn completions(
13585 &self,
13586 buffer: &Model<Buffer>,
13587 buffer_position: text::Anchor,
13588 trigger: CompletionContext,
13589 cx: &mut ViewContext<Editor>,
13590 ) -> Task<Result<Vec<Completion>>>;
13591
13592 fn resolve_completions(
13593 &self,
13594 buffer: Model<Buffer>,
13595 completion_indices: Vec<usize>,
13596 completions: Rc<RefCell<Box<[Completion]>>>,
13597 cx: &mut ViewContext<Editor>,
13598 ) -> Task<Result<bool>>;
13599
13600 fn apply_additional_edits_for_completion(
13601 &self,
13602 _buffer: Model<Buffer>,
13603 _completions: Rc<RefCell<Box<[Completion]>>>,
13604 _completion_index: usize,
13605 _push_to_history: bool,
13606 _cx: &mut ViewContext<Editor>,
13607 ) -> Task<Result<Option<language::Transaction>>> {
13608 Task::ready(Ok(None))
13609 }
13610
13611 fn is_completion_trigger(
13612 &self,
13613 buffer: &Model<Buffer>,
13614 position: language::Anchor,
13615 text: &str,
13616 trigger_in_words: bool,
13617 cx: &mut ViewContext<Editor>,
13618 ) -> bool;
13619
13620 fn sort_completions(&self) -> bool {
13621 true
13622 }
13623}
13624
13625pub trait CodeActionProvider {
13626 fn id(&self) -> Arc<str>;
13627
13628 fn code_actions(
13629 &self,
13630 buffer: &Model<Buffer>,
13631 range: Range<text::Anchor>,
13632 cx: &mut WindowContext,
13633 ) -> Task<Result<Vec<CodeAction>>>;
13634
13635 fn apply_code_action(
13636 &self,
13637 buffer_handle: Model<Buffer>,
13638 action: CodeAction,
13639 excerpt_id: ExcerptId,
13640 push_to_history: bool,
13641 cx: &mut WindowContext,
13642 ) -> Task<Result<ProjectTransaction>>;
13643}
13644
13645impl CodeActionProvider for Model<Project> {
13646 fn id(&self) -> Arc<str> {
13647 "project".into()
13648 }
13649
13650 fn code_actions(
13651 &self,
13652 buffer: &Model<Buffer>,
13653 range: Range<text::Anchor>,
13654 cx: &mut WindowContext,
13655 ) -> Task<Result<Vec<CodeAction>>> {
13656 self.update(cx, |project, cx| {
13657 project.code_actions(buffer, range, None, cx)
13658 })
13659 }
13660
13661 fn apply_code_action(
13662 &self,
13663 buffer_handle: Model<Buffer>,
13664 action: CodeAction,
13665 _excerpt_id: ExcerptId,
13666 push_to_history: bool,
13667 cx: &mut WindowContext,
13668 ) -> Task<Result<ProjectTransaction>> {
13669 self.update(cx, |project, cx| {
13670 project.apply_code_action(buffer_handle, action, push_to_history, cx)
13671 })
13672 }
13673}
13674
13675fn snippet_completions(
13676 project: &Project,
13677 buffer: &Model<Buffer>,
13678 buffer_position: text::Anchor,
13679 cx: &mut AppContext,
13680) -> Task<Result<Vec<Completion>>> {
13681 let language = buffer.read(cx).language_at(buffer_position);
13682 let language_name = language.as_ref().map(|language| language.lsp_id());
13683 let snippet_store = project.snippets().read(cx);
13684 let snippets = snippet_store.snippets_for(language_name, cx);
13685
13686 if snippets.is_empty() {
13687 return Task::ready(Ok(vec![]));
13688 }
13689 let snapshot = buffer.read(cx).text_snapshot();
13690 let chars: String = snapshot
13691 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
13692 .collect();
13693
13694 let scope = language.map(|language| language.default_scope());
13695 let executor = cx.background_executor().clone();
13696
13697 cx.background_executor().spawn(async move {
13698 let classifier = CharClassifier::new(scope).for_completion(true);
13699 let mut last_word = chars
13700 .chars()
13701 .take_while(|c| classifier.is_word(*c))
13702 .collect::<String>();
13703 last_word = last_word.chars().rev().collect();
13704
13705 if last_word.is_empty() {
13706 return Ok(vec![]);
13707 }
13708
13709 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
13710 let to_lsp = |point: &text::Anchor| {
13711 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
13712 point_to_lsp(end)
13713 };
13714 let lsp_end = to_lsp(&buffer_position);
13715
13716 let candidates = snippets
13717 .iter()
13718 .enumerate()
13719 .flat_map(|(ix, snippet)| {
13720 snippet
13721 .prefix
13722 .iter()
13723 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
13724 })
13725 .collect::<Vec<StringMatchCandidate>>();
13726
13727 let mut matches = fuzzy::match_strings(
13728 &candidates,
13729 &last_word,
13730 last_word.chars().any(|c| c.is_uppercase()),
13731 100,
13732 &Default::default(),
13733 executor,
13734 )
13735 .await;
13736
13737 // Remove all candidates where the query's start does not match the start of any word in the candidate
13738 if let Some(query_start) = last_word.chars().next() {
13739 matches.retain(|string_match| {
13740 split_words(&string_match.string).any(|word| {
13741 // Check that the first codepoint of the word as lowercase matches the first
13742 // codepoint of the query as lowercase
13743 word.chars()
13744 .flat_map(|codepoint| codepoint.to_lowercase())
13745 .zip(query_start.to_lowercase())
13746 .all(|(word_cp, query_cp)| word_cp == query_cp)
13747 })
13748 });
13749 }
13750
13751 let matched_strings = matches
13752 .into_iter()
13753 .map(|m| m.string)
13754 .collect::<HashSet<_>>();
13755
13756 let result: Vec<Completion> = snippets
13757 .into_iter()
13758 .filter_map(|snippet| {
13759 let matching_prefix = snippet
13760 .prefix
13761 .iter()
13762 .find(|prefix| matched_strings.contains(*prefix))?;
13763 let start = as_offset - last_word.len();
13764 let start = snapshot.anchor_before(start);
13765 let range = start..buffer_position;
13766 let lsp_start = to_lsp(&start);
13767 let lsp_range = lsp::Range {
13768 start: lsp_start,
13769 end: lsp_end,
13770 };
13771 Some(Completion {
13772 old_range: range,
13773 new_text: snippet.body.clone(),
13774 resolved: false,
13775 label: CodeLabel {
13776 text: matching_prefix.clone(),
13777 runs: vec![],
13778 filter_range: 0..matching_prefix.len(),
13779 },
13780 server_id: LanguageServerId(usize::MAX),
13781 documentation: snippet.description.clone().map(Documentation::SingleLine),
13782 lsp_completion: lsp::CompletionItem {
13783 label: snippet.prefix.first().unwrap().clone(),
13784 kind: Some(CompletionItemKind::SNIPPET),
13785 label_details: snippet.description.as_ref().map(|description| {
13786 lsp::CompletionItemLabelDetails {
13787 detail: Some(description.clone()),
13788 description: None,
13789 }
13790 }),
13791 insert_text_format: Some(InsertTextFormat::SNIPPET),
13792 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
13793 lsp::InsertReplaceEdit {
13794 new_text: snippet.body.clone(),
13795 insert: lsp_range,
13796 replace: lsp_range,
13797 },
13798 )),
13799 filter_text: Some(snippet.body.clone()),
13800 sort_text: Some(char::MAX.to_string()),
13801 ..Default::default()
13802 },
13803 confirm: None,
13804 })
13805 })
13806 .collect();
13807
13808 Ok(result)
13809 })
13810}
13811
13812impl CompletionProvider for Model<Project> {
13813 fn completions(
13814 &self,
13815 buffer: &Model<Buffer>,
13816 buffer_position: text::Anchor,
13817 options: CompletionContext,
13818 cx: &mut ViewContext<Editor>,
13819 ) -> Task<Result<Vec<Completion>>> {
13820 self.update(cx, |project, cx| {
13821 let snippets = snippet_completions(project, buffer, buffer_position, cx);
13822 let project_completions = project.completions(buffer, buffer_position, options, cx);
13823 cx.background_executor().spawn(async move {
13824 let mut completions = project_completions.await?;
13825 let snippets_completions = snippets.await?;
13826 completions.extend(snippets_completions);
13827 Ok(completions)
13828 })
13829 })
13830 }
13831
13832 fn resolve_completions(
13833 &self,
13834 buffer: Model<Buffer>,
13835 completion_indices: Vec<usize>,
13836 completions: Rc<RefCell<Box<[Completion]>>>,
13837 cx: &mut ViewContext<Editor>,
13838 ) -> Task<Result<bool>> {
13839 self.update(cx, |project, cx| {
13840 project.lsp_store().update(cx, |lsp_store, cx| {
13841 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
13842 })
13843 })
13844 }
13845
13846 fn apply_additional_edits_for_completion(
13847 &self,
13848 buffer: Model<Buffer>,
13849 completions: Rc<RefCell<Box<[Completion]>>>,
13850 completion_index: usize,
13851 push_to_history: bool,
13852 cx: &mut ViewContext<Editor>,
13853 ) -> Task<Result<Option<language::Transaction>>> {
13854 self.update(cx, |project, cx| {
13855 project.lsp_store().update(cx, |lsp_store, cx| {
13856 lsp_store.apply_additional_edits_for_completion(
13857 buffer,
13858 completions,
13859 completion_index,
13860 push_to_history,
13861 cx,
13862 )
13863 })
13864 })
13865 }
13866
13867 fn is_completion_trigger(
13868 &self,
13869 buffer: &Model<Buffer>,
13870 position: language::Anchor,
13871 text: &str,
13872 trigger_in_words: bool,
13873 cx: &mut ViewContext<Editor>,
13874 ) -> bool {
13875 let mut chars = text.chars();
13876 let char = if let Some(char) = chars.next() {
13877 char
13878 } else {
13879 return false;
13880 };
13881 if chars.next().is_some() {
13882 return false;
13883 }
13884
13885 let buffer = buffer.read(cx);
13886 let snapshot = buffer.snapshot();
13887 if !snapshot.settings_at(position, cx).show_completions_on_input {
13888 return false;
13889 }
13890 let classifier = snapshot.char_classifier_at(position).for_completion(true);
13891 if trigger_in_words && classifier.is_word(char) {
13892 return true;
13893 }
13894
13895 buffer.completion_triggers().contains(text)
13896 }
13897}
13898
13899impl SemanticsProvider for Model<Project> {
13900 fn hover(
13901 &self,
13902 buffer: &Model<Buffer>,
13903 position: text::Anchor,
13904 cx: &mut AppContext,
13905 ) -> Option<Task<Vec<project::Hover>>> {
13906 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
13907 }
13908
13909 fn document_highlights(
13910 &self,
13911 buffer: &Model<Buffer>,
13912 position: text::Anchor,
13913 cx: &mut AppContext,
13914 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
13915 Some(self.update(cx, |project, cx| {
13916 project.document_highlights(buffer, position, cx)
13917 }))
13918 }
13919
13920 fn definitions(
13921 &self,
13922 buffer: &Model<Buffer>,
13923 position: text::Anchor,
13924 kind: GotoDefinitionKind,
13925 cx: &mut AppContext,
13926 ) -> Option<Task<Result<Vec<LocationLink>>>> {
13927 Some(self.update(cx, |project, cx| match kind {
13928 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
13929 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
13930 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
13931 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
13932 }))
13933 }
13934
13935 fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
13936 // TODO: make this work for remote projects
13937 self.read(cx)
13938 .language_servers_for_local_buffer(buffer.read(cx), cx)
13939 .any(
13940 |(_, server)| match server.capabilities().inlay_hint_provider {
13941 Some(lsp::OneOf::Left(enabled)) => enabled,
13942 Some(lsp::OneOf::Right(_)) => true,
13943 None => false,
13944 },
13945 )
13946 }
13947
13948 fn inlay_hints(
13949 &self,
13950 buffer_handle: Model<Buffer>,
13951 range: Range<text::Anchor>,
13952 cx: &mut AppContext,
13953 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
13954 Some(self.update(cx, |project, cx| {
13955 project.inlay_hints(buffer_handle, range, cx)
13956 }))
13957 }
13958
13959 fn resolve_inlay_hint(
13960 &self,
13961 hint: InlayHint,
13962 buffer_handle: Model<Buffer>,
13963 server_id: LanguageServerId,
13964 cx: &mut AppContext,
13965 ) -> Option<Task<anyhow::Result<InlayHint>>> {
13966 Some(self.update(cx, |project, cx| {
13967 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
13968 }))
13969 }
13970
13971 fn range_for_rename(
13972 &self,
13973 buffer: &Model<Buffer>,
13974 position: text::Anchor,
13975 cx: &mut AppContext,
13976 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
13977 Some(self.update(cx, |project, cx| {
13978 project.prepare_rename(buffer.clone(), position, cx)
13979 }))
13980 }
13981
13982 fn perform_rename(
13983 &self,
13984 buffer: &Model<Buffer>,
13985 position: text::Anchor,
13986 new_name: String,
13987 cx: &mut AppContext,
13988 ) -> Option<Task<Result<ProjectTransaction>>> {
13989 Some(self.update(cx, |project, cx| {
13990 project.perform_rename(buffer.clone(), position, new_name, cx)
13991 }))
13992 }
13993}
13994
13995fn inlay_hint_settings(
13996 location: Anchor,
13997 snapshot: &MultiBufferSnapshot,
13998 cx: &mut ViewContext<Editor>,
13999) -> InlayHintSettings {
14000 let file = snapshot.file_at(location);
14001 let language = snapshot.language_at(location).map(|l| l.name());
14002 language_settings(language, file, cx).inlay_hints
14003}
14004
14005fn consume_contiguous_rows(
14006 contiguous_row_selections: &mut Vec<Selection<Point>>,
14007 selection: &Selection<Point>,
14008 display_map: &DisplaySnapshot,
14009 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
14010) -> (MultiBufferRow, MultiBufferRow) {
14011 contiguous_row_selections.push(selection.clone());
14012 let start_row = MultiBufferRow(selection.start.row);
14013 let mut end_row = ending_row(selection, display_map);
14014
14015 while let Some(next_selection) = selections.peek() {
14016 if next_selection.start.row <= end_row.0 {
14017 end_row = ending_row(next_selection, display_map);
14018 contiguous_row_selections.push(selections.next().unwrap().clone());
14019 } else {
14020 break;
14021 }
14022 }
14023 (start_row, end_row)
14024}
14025
14026fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
14027 if next_selection.end.column > 0 || next_selection.is_empty() {
14028 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
14029 } else {
14030 MultiBufferRow(next_selection.end.row)
14031 }
14032}
14033
14034impl EditorSnapshot {
14035 pub fn remote_selections_in_range<'a>(
14036 &'a self,
14037 range: &'a Range<Anchor>,
14038 collaboration_hub: &dyn CollaborationHub,
14039 cx: &'a AppContext,
14040 ) -> impl 'a + Iterator<Item = RemoteSelection> {
14041 let participant_names = collaboration_hub.user_names(cx);
14042 let participant_indices = collaboration_hub.user_participant_indices(cx);
14043 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
14044 let collaborators_by_replica_id = collaborators_by_peer_id
14045 .iter()
14046 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
14047 .collect::<HashMap<_, _>>();
14048 self.buffer_snapshot
14049 .selections_in_range(range, false)
14050 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
14051 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
14052 let participant_index = participant_indices.get(&collaborator.user_id).copied();
14053 let user_name = participant_names.get(&collaborator.user_id).cloned();
14054 Some(RemoteSelection {
14055 replica_id,
14056 selection,
14057 cursor_shape,
14058 line_mode,
14059 participant_index,
14060 peer_id: collaborator.peer_id,
14061 user_name,
14062 })
14063 })
14064 }
14065
14066 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
14067 self.display_snapshot.buffer_snapshot.language_at(position)
14068 }
14069
14070 pub fn is_focused(&self) -> bool {
14071 self.is_focused
14072 }
14073
14074 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
14075 self.placeholder_text.as_ref()
14076 }
14077
14078 pub fn scroll_position(&self) -> gpui::Point<f32> {
14079 self.scroll_anchor.scroll_position(&self.display_snapshot)
14080 }
14081
14082 fn gutter_dimensions(
14083 &self,
14084 font_id: FontId,
14085 font_size: Pixels,
14086 em_width: Pixels,
14087 em_advance: Pixels,
14088 max_line_number_width: Pixels,
14089 cx: &AppContext,
14090 ) -> GutterDimensions {
14091 if !self.show_gutter {
14092 return GutterDimensions::default();
14093 }
14094 let descent = cx.text_system().descent(font_id, font_size);
14095
14096 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
14097 matches!(
14098 ProjectSettings::get_global(cx).git.git_gutter,
14099 Some(GitGutterSetting::TrackedFiles)
14100 )
14101 });
14102 let gutter_settings = EditorSettings::get_global(cx).gutter;
14103 let show_line_numbers = self
14104 .show_line_numbers
14105 .unwrap_or(gutter_settings.line_numbers);
14106 let line_gutter_width = if show_line_numbers {
14107 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
14108 let min_width_for_number_on_gutter = em_advance * 4.0;
14109 max_line_number_width.max(min_width_for_number_on_gutter)
14110 } else {
14111 0.0.into()
14112 };
14113
14114 let show_code_actions = self
14115 .show_code_actions
14116 .unwrap_or(gutter_settings.code_actions);
14117
14118 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
14119
14120 let git_blame_entries_width =
14121 self.git_blame_gutter_max_author_length
14122 .map(|max_author_length| {
14123 // Length of the author name, but also space for the commit hash,
14124 // the spacing and the timestamp.
14125 let max_char_count = max_author_length
14126 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
14127 + 7 // length of commit sha
14128 + 14 // length of max relative timestamp ("60 minutes ago")
14129 + 4; // gaps and margins
14130
14131 em_advance * max_char_count
14132 });
14133
14134 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
14135 left_padding += if show_code_actions || show_runnables {
14136 em_width * 3.0
14137 } else if show_git_gutter && show_line_numbers {
14138 em_width * 2.0
14139 } else if show_git_gutter || show_line_numbers {
14140 em_width
14141 } else {
14142 px(0.)
14143 };
14144
14145 let right_padding = if gutter_settings.folds && show_line_numbers {
14146 em_width * 4.0
14147 } else if gutter_settings.folds {
14148 em_width * 3.0
14149 } else if show_line_numbers {
14150 em_width
14151 } else {
14152 px(0.)
14153 };
14154
14155 GutterDimensions {
14156 left_padding,
14157 right_padding,
14158 width: line_gutter_width + left_padding + right_padding,
14159 margin: -descent,
14160 git_blame_entries_width,
14161 }
14162 }
14163
14164 pub fn render_crease_toggle(
14165 &self,
14166 buffer_row: MultiBufferRow,
14167 row_contains_cursor: bool,
14168 editor: View<Editor>,
14169 cx: &mut WindowContext,
14170 ) -> Option<AnyElement> {
14171 let folded = self.is_line_folded(buffer_row);
14172 let mut is_foldable = false;
14173
14174 if let Some(crease) = self
14175 .crease_snapshot
14176 .query_row(buffer_row, &self.buffer_snapshot)
14177 {
14178 is_foldable = true;
14179 match crease {
14180 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
14181 if let Some(render_toggle) = render_toggle {
14182 let toggle_callback = Arc::new(move |folded, cx: &mut WindowContext| {
14183 if folded {
14184 editor.update(cx, |editor, cx| {
14185 editor.fold_at(&crate::FoldAt { buffer_row }, cx)
14186 });
14187 } else {
14188 editor.update(cx, |editor, cx| {
14189 editor.unfold_at(&crate::UnfoldAt { buffer_row }, cx)
14190 });
14191 }
14192 });
14193 return Some((render_toggle)(buffer_row, folded, toggle_callback, cx));
14194 }
14195 }
14196 }
14197 }
14198
14199 is_foldable |= self.starts_indent(buffer_row);
14200
14201 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
14202 Some(
14203 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
14204 .toggle_state(folded)
14205 .on_click(cx.listener_for(&editor, move |this, _e, cx| {
14206 if folded {
14207 this.unfold_at(&UnfoldAt { buffer_row }, cx);
14208 } else {
14209 this.fold_at(&FoldAt { buffer_row }, cx);
14210 }
14211 }))
14212 .into_any_element(),
14213 )
14214 } else {
14215 None
14216 }
14217 }
14218
14219 pub fn render_crease_trailer(
14220 &self,
14221 buffer_row: MultiBufferRow,
14222 cx: &mut WindowContext,
14223 ) -> Option<AnyElement> {
14224 let folded = self.is_line_folded(buffer_row);
14225 if let Crease::Inline { render_trailer, .. } = self
14226 .crease_snapshot
14227 .query_row(buffer_row, &self.buffer_snapshot)?
14228 {
14229 let render_trailer = render_trailer.as_ref()?;
14230 Some(render_trailer(buffer_row, folded, cx))
14231 } else {
14232 None
14233 }
14234 }
14235}
14236
14237impl Deref for EditorSnapshot {
14238 type Target = DisplaySnapshot;
14239
14240 fn deref(&self) -> &Self::Target {
14241 &self.display_snapshot
14242 }
14243}
14244
14245#[derive(Clone, Debug, PartialEq, Eq)]
14246pub enum EditorEvent {
14247 InputIgnored {
14248 text: Arc<str>,
14249 },
14250 InputHandled {
14251 utf16_range_to_replace: Option<Range<isize>>,
14252 text: Arc<str>,
14253 },
14254 ExcerptsAdded {
14255 buffer: Model<Buffer>,
14256 predecessor: ExcerptId,
14257 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
14258 },
14259 ExcerptsRemoved {
14260 ids: Vec<ExcerptId>,
14261 },
14262 BufferFoldToggled {
14263 ids: Vec<ExcerptId>,
14264 folded: bool,
14265 },
14266 ExcerptsEdited {
14267 ids: Vec<ExcerptId>,
14268 },
14269 ExcerptsExpanded {
14270 ids: Vec<ExcerptId>,
14271 },
14272 BufferEdited,
14273 Edited {
14274 transaction_id: clock::Lamport,
14275 },
14276 Reparsed(BufferId),
14277 Focused,
14278 FocusedIn,
14279 Blurred,
14280 DirtyChanged,
14281 Saved,
14282 TitleChanged,
14283 DiffBaseChanged,
14284 SelectionsChanged {
14285 local: bool,
14286 },
14287 ScrollPositionChanged {
14288 local: bool,
14289 autoscroll: bool,
14290 },
14291 Closed,
14292 TransactionUndone {
14293 transaction_id: clock::Lamport,
14294 },
14295 TransactionBegun {
14296 transaction_id: clock::Lamport,
14297 },
14298 Reloaded,
14299 CursorShapeChanged,
14300}
14301
14302impl EventEmitter<EditorEvent> for Editor {}
14303
14304impl FocusableView for Editor {
14305 fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
14306 self.focus_handle.clone()
14307 }
14308}
14309
14310impl Render for Editor {
14311 fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
14312 let settings = ThemeSettings::get_global(cx);
14313
14314 let mut text_style = match self.mode {
14315 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
14316 color: cx.theme().colors().editor_foreground,
14317 font_family: settings.ui_font.family.clone(),
14318 font_features: settings.ui_font.features.clone(),
14319 font_fallbacks: settings.ui_font.fallbacks.clone(),
14320 font_size: rems(0.875).into(),
14321 font_weight: settings.ui_font.weight,
14322 line_height: relative(settings.buffer_line_height.value()),
14323 ..Default::default()
14324 },
14325 EditorMode::Full => TextStyle {
14326 color: cx.theme().colors().editor_foreground,
14327 font_family: settings.buffer_font.family.clone(),
14328 font_features: settings.buffer_font.features.clone(),
14329 font_fallbacks: settings.buffer_font.fallbacks.clone(),
14330 font_size: settings.buffer_font_size(cx).into(),
14331 font_weight: settings.buffer_font.weight,
14332 line_height: relative(settings.buffer_line_height.value()),
14333 ..Default::default()
14334 },
14335 };
14336 if let Some(text_style_refinement) = &self.text_style_refinement {
14337 text_style.refine(text_style_refinement)
14338 }
14339
14340 let background = match self.mode {
14341 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
14342 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
14343 EditorMode::Full => cx.theme().colors().editor_background,
14344 };
14345
14346 EditorElement::new(
14347 cx.view(),
14348 EditorStyle {
14349 background,
14350 local_player: cx.theme().players().local(),
14351 text: text_style,
14352 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
14353 syntax: cx.theme().syntax().clone(),
14354 status: cx.theme().status().clone(),
14355 inlay_hints_style: make_inlay_hints_style(cx),
14356 inline_completion_styles: make_suggestion_styles(cx),
14357 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
14358 },
14359 )
14360 }
14361}
14362
14363impl ViewInputHandler for Editor {
14364 fn text_for_range(
14365 &mut self,
14366 range_utf16: Range<usize>,
14367 adjusted_range: &mut Option<Range<usize>>,
14368 cx: &mut ViewContext<Self>,
14369 ) -> Option<String> {
14370 let snapshot = self.buffer.read(cx).read(cx);
14371 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
14372 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
14373 if (start.0..end.0) != range_utf16 {
14374 adjusted_range.replace(start.0..end.0);
14375 }
14376 Some(snapshot.text_for_range(start..end).collect())
14377 }
14378
14379 fn selected_text_range(
14380 &mut self,
14381 ignore_disabled_input: bool,
14382 cx: &mut ViewContext<Self>,
14383 ) -> Option<UTF16Selection> {
14384 // Prevent the IME menu from appearing when holding down an alphabetic key
14385 // while input is disabled.
14386 if !ignore_disabled_input && !self.input_enabled {
14387 return None;
14388 }
14389
14390 let selection = self.selections.newest::<OffsetUtf16>(cx);
14391 let range = selection.range();
14392
14393 Some(UTF16Selection {
14394 range: range.start.0..range.end.0,
14395 reversed: selection.reversed,
14396 })
14397 }
14398
14399 fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>> {
14400 let snapshot = self.buffer.read(cx).read(cx);
14401 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
14402 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
14403 }
14404
14405 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
14406 self.clear_highlights::<InputComposition>(cx);
14407 self.ime_transaction.take();
14408 }
14409
14410 fn replace_text_in_range(
14411 &mut self,
14412 range_utf16: Option<Range<usize>>,
14413 text: &str,
14414 cx: &mut ViewContext<Self>,
14415 ) {
14416 if !self.input_enabled {
14417 cx.emit(EditorEvent::InputIgnored { text: text.into() });
14418 return;
14419 }
14420
14421 self.transact(cx, |this, cx| {
14422 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
14423 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14424 Some(this.selection_replacement_ranges(range_utf16, cx))
14425 } else {
14426 this.marked_text_ranges(cx)
14427 };
14428
14429 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
14430 let newest_selection_id = this.selections.newest_anchor().id;
14431 this.selections
14432 .all::<OffsetUtf16>(cx)
14433 .iter()
14434 .zip(ranges_to_replace.iter())
14435 .find_map(|(selection, range)| {
14436 if selection.id == newest_selection_id {
14437 Some(
14438 (range.start.0 as isize - selection.head().0 as isize)
14439 ..(range.end.0 as isize - selection.head().0 as isize),
14440 )
14441 } else {
14442 None
14443 }
14444 })
14445 });
14446
14447 cx.emit(EditorEvent::InputHandled {
14448 utf16_range_to_replace: range_to_replace,
14449 text: text.into(),
14450 });
14451
14452 if let Some(new_selected_ranges) = new_selected_ranges {
14453 this.change_selections(None, cx, |selections| {
14454 selections.select_ranges(new_selected_ranges)
14455 });
14456 this.backspace(&Default::default(), cx);
14457 }
14458
14459 this.handle_input(text, cx);
14460 });
14461
14462 if let Some(transaction) = self.ime_transaction {
14463 self.buffer.update(cx, |buffer, cx| {
14464 buffer.group_until_transaction(transaction, cx);
14465 });
14466 }
14467
14468 self.unmark_text(cx);
14469 }
14470
14471 fn replace_and_mark_text_in_range(
14472 &mut self,
14473 range_utf16: Option<Range<usize>>,
14474 text: &str,
14475 new_selected_range_utf16: Option<Range<usize>>,
14476 cx: &mut ViewContext<Self>,
14477 ) {
14478 if !self.input_enabled {
14479 return;
14480 }
14481
14482 let transaction = self.transact(cx, |this, cx| {
14483 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
14484 let snapshot = this.buffer.read(cx).read(cx);
14485 if let Some(relative_range_utf16) = range_utf16.as_ref() {
14486 for marked_range in &mut marked_ranges {
14487 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
14488 marked_range.start.0 += relative_range_utf16.start;
14489 marked_range.start =
14490 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
14491 marked_range.end =
14492 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
14493 }
14494 }
14495 Some(marked_ranges)
14496 } else if let Some(range_utf16) = range_utf16 {
14497 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
14498 Some(this.selection_replacement_ranges(range_utf16, cx))
14499 } else {
14500 None
14501 };
14502
14503 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
14504 let newest_selection_id = this.selections.newest_anchor().id;
14505 this.selections
14506 .all::<OffsetUtf16>(cx)
14507 .iter()
14508 .zip(ranges_to_replace.iter())
14509 .find_map(|(selection, range)| {
14510 if selection.id == newest_selection_id {
14511 Some(
14512 (range.start.0 as isize - selection.head().0 as isize)
14513 ..(range.end.0 as isize - selection.head().0 as isize),
14514 )
14515 } else {
14516 None
14517 }
14518 })
14519 });
14520
14521 cx.emit(EditorEvent::InputHandled {
14522 utf16_range_to_replace: range_to_replace,
14523 text: text.into(),
14524 });
14525
14526 if let Some(ranges) = ranges_to_replace {
14527 this.change_selections(None, cx, |s| s.select_ranges(ranges));
14528 }
14529
14530 let marked_ranges = {
14531 let snapshot = this.buffer.read(cx).read(cx);
14532 this.selections
14533 .disjoint_anchors()
14534 .iter()
14535 .map(|selection| {
14536 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
14537 })
14538 .collect::<Vec<_>>()
14539 };
14540
14541 if text.is_empty() {
14542 this.unmark_text(cx);
14543 } else {
14544 this.highlight_text::<InputComposition>(
14545 marked_ranges.clone(),
14546 HighlightStyle {
14547 underline: Some(UnderlineStyle {
14548 thickness: px(1.),
14549 color: None,
14550 wavy: false,
14551 }),
14552 ..Default::default()
14553 },
14554 cx,
14555 );
14556 }
14557
14558 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
14559 let use_autoclose = this.use_autoclose;
14560 let use_auto_surround = this.use_auto_surround;
14561 this.set_use_autoclose(false);
14562 this.set_use_auto_surround(false);
14563 this.handle_input(text, cx);
14564 this.set_use_autoclose(use_autoclose);
14565 this.set_use_auto_surround(use_auto_surround);
14566
14567 if let Some(new_selected_range) = new_selected_range_utf16 {
14568 let snapshot = this.buffer.read(cx).read(cx);
14569 let new_selected_ranges = marked_ranges
14570 .into_iter()
14571 .map(|marked_range| {
14572 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
14573 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
14574 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
14575 snapshot.clip_offset_utf16(new_start, Bias::Left)
14576 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
14577 })
14578 .collect::<Vec<_>>();
14579
14580 drop(snapshot);
14581 this.change_selections(None, cx, |selections| {
14582 selections.select_ranges(new_selected_ranges)
14583 });
14584 }
14585 });
14586
14587 self.ime_transaction = self.ime_transaction.or(transaction);
14588 if let Some(transaction) = self.ime_transaction {
14589 self.buffer.update(cx, |buffer, cx| {
14590 buffer.group_until_transaction(transaction, cx);
14591 });
14592 }
14593
14594 if self.text_highlights::<InputComposition>(cx).is_none() {
14595 self.ime_transaction.take();
14596 }
14597 }
14598
14599 fn bounds_for_range(
14600 &mut self,
14601 range_utf16: Range<usize>,
14602 element_bounds: gpui::Bounds<Pixels>,
14603 cx: &mut ViewContext<Self>,
14604 ) -> Option<gpui::Bounds<Pixels>> {
14605 let text_layout_details = self.text_layout_details(cx);
14606 let gpui::Point {
14607 x: em_width,
14608 y: line_height,
14609 } = self.character_size(cx);
14610
14611 let snapshot = self.snapshot(cx);
14612 let scroll_position = snapshot.scroll_position();
14613 let scroll_left = scroll_position.x * em_width;
14614
14615 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
14616 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
14617 + self.gutter_dimensions.width
14618 + self.gutter_dimensions.margin;
14619 let y = line_height * (start.row().as_f32() - scroll_position.y);
14620
14621 Some(Bounds {
14622 origin: element_bounds.origin + point(x, y),
14623 size: size(em_width, line_height),
14624 })
14625 }
14626}
14627
14628trait SelectionExt {
14629 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
14630 fn spanned_rows(
14631 &self,
14632 include_end_if_at_line_start: bool,
14633 map: &DisplaySnapshot,
14634 ) -> Range<MultiBufferRow>;
14635}
14636
14637impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
14638 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
14639 let start = self
14640 .start
14641 .to_point(&map.buffer_snapshot)
14642 .to_display_point(map);
14643 let end = self
14644 .end
14645 .to_point(&map.buffer_snapshot)
14646 .to_display_point(map);
14647 if self.reversed {
14648 end..start
14649 } else {
14650 start..end
14651 }
14652 }
14653
14654 fn spanned_rows(
14655 &self,
14656 include_end_if_at_line_start: bool,
14657 map: &DisplaySnapshot,
14658 ) -> Range<MultiBufferRow> {
14659 let start = self.start.to_point(&map.buffer_snapshot);
14660 let mut end = self.end.to_point(&map.buffer_snapshot);
14661 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
14662 end.row -= 1;
14663 }
14664
14665 let buffer_start = map.prev_line_boundary(start).0;
14666 let buffer_end = map.next_line_boundary(end).0;
14667 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
14668 }
14669}
14670
14671impl<T: InvalidationRegion> InvalidationStack<T> {
14672 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
14673 where
14674 S: Clone + ToOffset,
14675 {
14676 while let Some(region) = self.last() {
14677 let all_selections_inside_invalidation_ranges =
14678 if selections.len() == region.ranges().len() {
14679 selections
14680 .iter()
14681 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
14682 .all(|(selection, invalidation_range)| {
14683 let head = selection.head().to_offset(buffer);
14684 invalidation_range.start <= head && invalidation_range.end >= head
14685 })
14686 } else {
14687 false
14688 };
14689
14690 if all_selections_inside_invalidation_ranges {
14691 break;
14692 } else {
14693 self.pop();
14694 }
14695 }
14696 }
14697}
14698
14699impl<T> Default for InvalidationStack<T> {
14700 fn default() -> Self {
14701 Self(Default::default())
14702 }
14703}
14704
14705impl<T> Deref for InvalidationStack<T> {
14706 type Target = Vec<T>;
14707
14708 fn deref(&self) -> &Self::Target {
14709 &self.0
14710 }
14711}
14712
14713impl<T> DerefMut for InvalidationStack<T> {
14714 fn deref_mut(&mut self) -> &mut Self::Target {
14715 &mut self.0
14716 }
14717}
14718
14719impl InvalidationRegion for SnippetState {
14720 fn ranges(&self) -> &[Range<Anchor>] {
14721 &self.ranges[self.active_index]
14722 }
14723}
14724
14725pub fn diagnostic_block_renderer(
14726 diagnostic: Diagnostic,
14727 max_message_rows: Option<u8>,
14728 allow_closing: bool,
14729 _is_valid: bool,
14730) -> RenderBlock {
14731 let (text_without_backticks, code_ranges) =
14732 highlight_diagnostic_message(&diagnostic, max_message_rows);
14733
14734 Arc::new(move |cx: &mut BlockContext| {
14735 let group_id: SharedString = cx.block_id.to_string().into();
14736
14737 let mut text_style = cx.text_style().clone();
14738 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
14739 let theme_settings = ThemeSettings::get_global(cx);
14740 text_style.font_family = theme_settings.buffer_font.family.clone();
14741 text_style.font_style = theme_settings.buffer_font.style;
14742 text_style.font_features = theme_settings.buffer_font.features.clone();
14743 text_style.font_weight = theme_settings.buffer_font.weight;
14744
14745 let multi_line_diagnostic = diagnostic.message.contains('\n');
14746
14747 let buttons = |diagnostic: &Diagnostic| {
14748 if multi_line_diagnostic {
14749 v_flex()
14750 } else {
14751 h_flex()
14752 }
14753 .when(allow_closing, |div| {
14754 div.children(diagnostic.is_primary.then(|| {
14755 IconButton::new("close-block", IconName::XCircle)
14756 .icon_color(Color::Muted)
14757 .size(ButtonSize::Compact)
14758 .style(ButtonStyle::Transparent)
14759 .visible_on_hover(group_id.clone())
14760 .on_click(move |_click, cx| cx.dispatch_action(Box::new(Cancel)))
14761 .tooltip(|cx| Tooltip::for_action("Close Diagnostics", &Cancel, cx))
14762 }))
14763 })
14764 .child(
14765 IconButton::new("copy-block", IconName::Copy)
14766 .icon_color(Color::Muted)
14767 .size(ButtonSize::Compact)
14768 .style(ButtonStyle::Transparent)
14769 .visible_on_hover(group_id.clone())
14770 .on_click({
14771 let message = diagnostic.message.clone();
14772 move |_click, cx| {
14773 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
14774 }
14775 })
14776 .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
14777 )
14778 };
14779
14780 let icon_size = buttons(&diagnostic)
14781 .into_any_element()
14782 .layout_as_root(AvailableSpace::min_size(), cx);
14783
14784 h_flex()
14785 .id(cx.block_id)
14786 .group(group_id.clone())
14787 .relative()
14788 .size_full()
14789 .block_mouse_down()
14790 .pl(cx.gutter_dimensions.width)
14791 .w(cx.max_width - cx.gutter_dimensions.full_width())
14792 .child(
14793 div()
14794 .flex()
14795 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
14796 .flex_shrink(),
14797 )
14798 .child(buttons(&diagnostic))
14799 .child(div().flex().flex_shrink_0().child(
14800 StyledText::new(text_without_backticks.clone()).with_highlights(
14801 &text_style,
14802 code_ranges.iter().map(|range| {
14803 (
14804 range.clone(),
14805 HighlightStyle {
14806 font_weight: Some(FontWeight::BOLD),
14807 ..Default::default()
14808 },
14809 )
14810 }),
14811 ),
14812 ))
14813 .into_any_element()
14814 })
14815}
14816
14817fn inline_completion_edit_text(
14818 editor_snapshot: &EditorSnapshot,
14819 edits: &Vec<(Range<Anchor>, String)>,
14820 include_deletions: bool,
14821 cx: &WindowContext,
14822) -> InlineCompletionText {
14823 let edit_start = edits
14824 .first()
14825 .unwrap()
14826 .0
14827 .start
14828 .to_display_point(editor_snapshot);
14829
14830 let mut text = String::new();
14831 let mut offset = DisplayPoint::new(edit_start.row(), 0).to_offset(editor_snapshot, Bias::Left);
14832 let mut highlights = Vec::new();
14833 for (old_range, new_text) in edits {
14834 let old_offset_range = old_range.to_offset(&editor_snapshot.buffer_snapshot);
14835 text.extend(
14836 editor_snapshot
14837 .buffer_snapshot
14838 .chunks(offset..old_offset_range.start, false)
14839 .map(|chunk| chunk.text),
14840 );
14841 offset = old_offset_range.end;
14842
14843 let start = text.len();
14844 let color = if include_deletions && new_text.is_empty() {
14845 text.extend(
14846 editor_snapshot
14847 .buffer_snapshot
14848 .chunks(old_offset_range.start..offset, false)
14849 .map(|chunk| chunk.text),
14850 );
14851 cx.theme().status().deleted_background
14852 } else {
14853 text.push_str(new_text);
14854 cx.theme().status().created_background
14855 };
14856 let end = text.len();
14857
14858 highlights.push((
14859 start..end,
14860 HighlightStyle {
14861 background_color: Some(color),
14862 ..Default::default()
14863 },
14864 ));
14865 }
14866
14867 let edit_end = edits
14868 .last()
14869 .unwrap()
14870 .0
14871 .end
14872 .to_display_point(editor_snapshot);
14873 let end_of_line = DisplayPoint::new(edit_end.row(), editor_snapshot.line_len(edit_end.row()))
14874 .to_offset(editor_snapshot, Bias::Right);
14875 text.extend(
14876 editor_snapshot
14877 .buffer_snapshot
14878 .chunks(offset..end_of_line, false)
14879 .map(|chunk| chunk.text),
14880 );
14881
14882 InlineCompletionText::Edit {
14883 text: text.into(),
14884 highlights,
14885 }
14886}
14887
14888pub fn highlight_diagnostic_message(
14889 diagnostic: &Diagnostic,
14890 mut max_message_rows: Option<u8>,
14891) -> (SharedString, Vec<Range<usize>>) {
14892 let mut text_without_backticks = String::new();
14893 let mut code_ranges = Vec::new();
14894
14895 if let Some(source) = &diagnostic.source {
14896 text_without_backticks.push_str(source);
14897 code_ranges.push(0..source.len());
14898 text_without_backticks.push_str(": ");
14899 }
14900
14901 let mut prev_offset = 0;
14902 let mut in_code_block = false;
14903 let has_row_limit = max_message_rows.is_some();
14904 let mut newline_indices = diagnostic
14905 .message
14906 .match_indices('\n')
14907 .filter(|_| has_row_limit)
14908 .map(|(ix, _)| ix)
14909 .fuse()
14910 .peekable();
14911
14912 for (quote_ix, _) in diagnostic
14913 .message
14914 .match_indices('`')
14915 .chain([(diagnostic.message.len(), "")])
14916 {
14917 let mut first_newline_ix = None;
14918 let mut last_newline_ix = None;
14919 while let Some(newline_ix) = newline_indices.peek() {
14920 if *newline_ix < quote_ix {
14921 if first_newline_ix.is_none() {
14922 first_newline_ix = Some(*newline_ix);
14923 }
14924 last_newline_ix = Some(*newline_ix);
14925
14926 if let Some(rows_left) = &mut max_message_rows {
14927 if *rows_left == 0 {
14928 break;
14929 } else {
14930 *rows_left -= 1;
14931 }
14932 }
14933 let _ = newline_indices.next();
14934 } else {
14935 break;
14936 }
14937 }
14938 let prev_len = text_without_backticks.len();
14939 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
14940 text_without_backticks.push_str(new_text);
14941 if in_code_block {
14942 code_ranges.push(prev_len..text_without_backticks.len());
14943 }
14944 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
14945 in_code_block = !in_code_block;
14946 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
14947 text_without_backticks.push_str("...");
14948 break;
14949 }
14950 }
14951
14952 (text_without_backticks.into(), code_ranges)
14953}
14954
14955fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
14956 match severity {
14957 DiagnosticSeverity::ERROR => colors.error,
14958 DiagnosticSeverity::WARNING => colors.warning,
14959 DiagnosticSeverity::INFORMATION => colors.info,
14960 DiagnosticSeverity::HINT => colors.info,
14961 _ => colors.ignored,
14962 }
14963}
14964
14965pub fn styled_runs_for_code_label<'a>(
14966 label: &'a CodeLabel,
14967 syntax_theme: &'a theme::SyntaxTheme,
14968) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
14969 let fade_out = HighlightStyle {
14970 fade_out: Some(0.35),
14971 ..Default::default()
14972 };
14973
14974 let mut prev_end = label.filter_range.end;
14975 label
14976 .runs
14977 .iter()
14978 .enumerate()
14979 .flat_map(move |(ix, (range, highlight_id))| {
14980 let style = if let Some(style) = highlight_id.style(syntax_theme) {
14981 style
14982 } else {
14983 return Default::default();
14984 };
14985 let mut muted_style = style;
14986 muted_style.highlight(fade_out);
14987
14988 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
14989 if range.start >= label.filter_range.end {
14990 if range.start > prev_end {
14991 runs.push((prev_end..range.start, fade_out));
14992 }
14993 runs.push((range.clone(), muted_style));
14994 } else if range.end <= label.filter_range.end {
14995 runs.push((range.clone(), style));
14996 } else {
14997 runs.push((range.start..label.filter_range.end, style));
14998 runs.push((label.filter_range.end..range.end, muted_style));
14999 }
15000 prev_end = cmp::max(prev_end, range.end);
15001
15002 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
15003 runs.push((prev_end..label.text.len(), fade_out));
15004 }
15005
15006 runs
15007 })
15008}
15009
15010pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
15011 let mut prev_index = 0;
15012 let mut prev_codepoint: Option<char> = None;
15013 text.char_indices()
15014 .chain([(text.len(), '\0')])
15015 .filter_map(move |(index, codepoint)| {
15016 let prev_codepoint = prev_codepoint.replace(codepoint)?;
15017 let is_boundary = index == text.len()
15018 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
15019 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
15020 if is_boundary {
15021 let chunk = &text[prev_index..index];
15022 prev_index = index;
15023 Some(chunk)
15024 } else {
15025 None
15026 }
15027 })
15028}
15029
15030pub trait RangeToAnchorExt: Sized {
15031 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
15032
15033 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
15034 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
15035 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
15036 }
15037}
15038
15039impl<T: ToOffset> RangeToAnchorExt for Range<T> {
15040 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
15041 let start_offset = self.start.to_offset(snapshot);
15042 let end_offset = self.end.to_offset(snapshot);
15043 if start_offset == end_offset {
15044 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
15045 } else {
15046 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
15047 }
15048 }
15049}
15050
15051pub trait RowExt {
15052 fn as_f32(&self) -> f32;
15053
15054 fn next_row(&self) -> Self;
15055
15056 fn previous_row(&self) -> Self;
15057
15058 fn minus(&self, other: Self) -> u32;
15059}
15060
15061impl RowExt for DisplayRow {
15062 fn as_f32(&self) -> f32 {
15063 self.0 as f32
15064 }
15065
15066 fn next_row(&self) -> Self {
15067 Self(self.0 + 1)
15068 }
15069
15070 fn previous_row(&self) -> Self {
15071 Self(self.0.saturating_sub(1))
15072 }
15073
15074 fn minus(&self, other: Self) -> u32 {
15075 self.0 - other.0
15076 }
15077}
15078
15079impl RowExt for MultiBufferRow {
15080 fn as_f32(&self) -> f32 {
15081 self.0 as f32
15082 }
15083
15084 fn next_row(&self) -> Self {
15085 Self(self.0 + 1)
15086 }
15087
15088 fn previous_row(&self) -> Self {
15089 Self(self.0.saturating_sub(1))
15090 }
15091
15092 fn minus(&self, other: Self) -> u32 {
15093 self.0 - other.0
15094 }
15095}
15096
15097trait RowRangeExt {
15098 type Row;
15099
15100 fn len(&self) -> usize;
15101
15102 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
15103}
15104
15105impl RowRangeExt for Range<MultiBufferRow> {
15106 type Row = MultiBufferRow;
15107
15108 fn len(&self) -> usize {
15109 (self.end.0 - self.start.0) as usize
15110 }
15111
15112 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
15113 (self.start.0..self.end.0).map(MultiBufferRow)
15114 }
15115}
15116
15117impl RowRangeExt for Range<DisplayRow> {
15118 type Row = DisplayRow;
15119
15120 fn len(&self) -> usize {
15121 (self.end.0 - self.start.0) as usize
15122 }
15123
15124 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
15125 (self.start.0..self.end.0).map(DisplayRow)
15126 }
15127}
15128
15129fn hunk_status(hunk: &MultiBufferDiffHunk) -> DiffHunkStatus {
15130 if hunk.diff_base_byte_range.is_empty() {
15131 DiffHunkStatus::Added
15132 } else if hunk.row_range.is_empty() {
15133 DiffHunkStatus::Removed
15134 } else {
15135 DiffHunkStatus::Modified
15136 }
15137}
15138
15139/// If select range has more than one line, we
15140/// just point the cursor to range.start.
15141fn check_multiline_range(buffer: &Buffer, range: Range<usize>) -> Range<usize> {
15142 if buffer.offset_to_point(range.start).row == buffer.offset_to_point(range.end).row {
15143 range
15144 } else {
15145 range.start..range.start
15146 }
15147}
15148
15149pub struct KillRing(ClipboardItem);
15150impl Global for KillRing {}
15151
15152const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);