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