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