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