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