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 let select_state = SelectNextState {
6052 query: AhoCorasick::new(&[query])?,
6053 wordwise: true,
6054 done: false,
6055 };
6056 select_next_match_ranges(
6057 self,
6058 selection.start..selection.end,
6059 replace_newest,
6060 autoscroll,
6061 cx,
6062 );
6063 self.select_next_state = Some(select_state);
6064 } else {
6065 let query = buffer
6066 .text_for_range(selection.start..selection.end)
6067 .collect::<String>();
6068 self.select_next_state = Some(SelectNextState {
6069 query: AhoCorasick::new(&[query])?,
6070 wordwise: false,
6071 done: false,
6072 });
6073 self.select_next_match_internal(display_map, replace_newest, autoscroll, cx)?;
6074 }
6075 }
6076 Ok(())
6077 }
6078
6079 pub fn select_all_matches(
6080 &mut self,
6081 action: &SelectAllMatches,
6082 cx: &mut ViewContext<Self>,
6083 ) -> Result<()> {
6084 self.push_to_selection_history();
6085 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6086
6087 loop {
6088 self.select_next_match_internal(&display_map, action.replace_newest, None, cx)?;
6089
6090 if self
6091 .select_next_state
6092 .as_ref()
6093 .map(|selection_state| selection_state.done)
6094 .unwrap_or(true)
6095 {
6096 break;
6097 }
6098 }
6099
6100 Ok(())
6101 }
6102
6103 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) -> Result<()> {
6104 self.push_to_selection_history();
6105 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6106 self.select_next_match_internal(
6107 &display_map,
6108 action.replace_newest,
6109 Some(Autoscroll::newest()),
6110 cx,
6111 )?;
6112 Ok(())
6113 }
6114
6115 pub fn select_previous(
6116 &mut self,
6117 action: &SelectPrevious,
6118 cx: &mut ViewContext<Self>,
6119 ) -> Result<()> {
6120 self.push_to_selection_history();
6121 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6122 let buffer = &display_map.buffer_snapshot;
6123 let mut selections = self.selections.all::<usize>(cx);
6124 if let Some(mut select_prev_state) = self.select_prev_state.take() {
6125 let query = &select_prev_state.query;
6126 if !select_prev_state.done {
6127 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
6128 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
6129 let mut next_selected_range = None;
6130 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
6131 let bytes_before_last_selection =
6132 buffer.reversed_bytes_in_range(0..last_selection.start);
6133 let bytes_after_first_selection =
6134 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
6135 let query_matches = query
6136 .stream_find_iter(bytes_before_last_selection)
6137 .map(|result| (last_selection.start, result))
6138 .chain(
6139 query
6140 .stream_find_iter(bytes_after_first_selection)
6141 .map(|result| (buffer.len(), result)),
6142 );
6143 for (end_offset, query_match) in query_matches {
6144 let query_match = query_match.unwrap(); // can only fail due to I/O
6145 let offset_range =
6146 end_offset - query_match.end()..end_offset - query_match.start();
6147 let display_range = offset_range.start.to_display_point(&display_map)
6148 ..offset_range.end.to_display_point(&display_map);
6149
6150 if !select_prev_state.wordwise
6151 || (!movement::is_inside_word(&display_map, display_range.start)
6152 && !movement::is_inside_word(&display_map, display_range.end))
6153 {
6154 next_selected_range = Some(offset_range);
6155 break;
6156 }
6157 }
6158
6159 if let Some(next_selected_range) = next_selected_range {
6160 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
6161 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6162 if action.replace_newest {
6163 s.delete(s.newest_anchor().id);
6164 }
6165 s.insert_range(next_selected_range);
6166 });
6167 } else {
6168 select_prev_state.done = true;
6169 }
6170 }
6171
6172 self.select_prev_state = Some(select_prev_state);
6173 } else if selections.len() == 1 {
6174 let selection = selections.last_mut().unwrap();
6175 if selection.start == selection.end {
6176 let word_range = movement::surrounding_word(
6177 &display_map,
6178 selection.start.to_display_point(&display_map),
6179 );
6180 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
6181 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
6182 selection.goal = SelectionGoal::None;
6183 selection.reversed = false;
6184
6185 let query = buffer
6186 .text_for_range(selection.start..selection.end)
6187 .collect::<String>();
6188 let query = query.chars().rev().collect::<String>();
6189 let select_state = SelectNextState {
6190 query: AhoCorasick::new(&[query])?,
6191 wordwise: true,
6192 done: false,
6193 };
6194 self.unfold_ranges([selection.start..selection.end], false, true, cx);
6195 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
6196 s.select(selections);
6197 });
6198 self.select_prev_state = Some(select_state);
6199 } else {
6200 let query = buffer
6201 .text_for_range(selection.start..selection.end)
6202 .collect::<String>();
6203 let query = query.chars().rev().collect::<String>();
6204 self.select_prev_state = Some(SelectNextState {
6205 query: AhoCorasick::new(&[query])?,
6206 wordwise: false,
6207 done: false,
6208 });
6209 self.select_previous(action, cx)?;
6210 }
6211 }
6212 Ok(())
6213 }
6214
6215 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
6216 self.transact(cx, |this, cx| {
6217 let mut selections = this.selections.all::<Point>(cx);
6218 let mut edits = Vec::new();
6219 let mut selection_edit_ranges = Vec::new();
6220 let mut last_toggled_row = None;
6221 let snapshot = this.buffer.read(cx).read(cx);
6222 let empty_str: Arc<str> = "".into();
6223 let mut suffixes_inserted = Vec::new();
6224
6225 fn comment_prefix_range(
6226 snapshot: &MultiBufferSnapshot,
6227 row: u32,
6228 comment_prefix: &str,
6229 comment_prefix_whitespace: &str,
6230 ) -> Range<Point> {
6231 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
6232
6233 let mut line_bytes = snapshot
6234 .bytes_in_range(start..snapshot.max_point())
6235 .flatten()
6236 .copied();
6237
6238 // If this line currently begins with the line comment prefix, then record
6239 // the range containing the prefix.
6240 if line_bytes
6241 .by_ref()
6242 .take(comment_prefix.len())
6243 .eq(comment_prefix.bytes())
6244 {
6245 // Include any whitespace that matches the comment prefix.
6246 let matching_whitespace_len = line_bytes
6247 .zip(comment_prefix_whitespace.bytes())
6248 .take_while(|(a, b)| a == b)
6249 .count() as u32;
6250 let end = Point::new(
6251 start.row,
6252 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
6253 );
6254 start..end
6255 } else {
6256 start..start
6257 }
6258 }
6259
6260 fn comment_suffix_range(
6261 snapshot: &MultiBufferSnapshot,
6262 row: u32,
6263 comment_suffix: &str,
6264 comment_suffix_has_leading_space: bool,
6265 ) -> Range<Point> {
6266 let end = Point::new(row, snapshot.line_len(row));
6267 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
6268
6269 let mut line_end_bytes = snapshot
6270 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
6271 .flatten()
6272 .copied();
6273
6274 let leading_space_len = if suffix_start_column > 0
6275 && line_end_bytes.next() == Some(b' ')
6276 && comment_suffix_has_leading_space
6277 {
6278 1
6279 } else {
6280 0
6281 };
6282
6283 // If this line currently begins with the line comment prefix, then record
6284 // the range containing the prefix.
6285 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
6286 let start = Point::new(end.row, suffix_start_column - leading_space_len);
6287 start..end
6288 } else {
6289 end..end
6290 }
6291 }
6292
6293 // TODO: Handle selections that cross excerpts
6294 for selection in &mut selections {
6295 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
6296 let language = if let Some(language) =
6297 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
6298 {
6299 language
6300 } else {
6301 continue;
6302 };
6303
6304 selection_edit_ranges.clear();
6305
6306 // If multiple selections contain a given row, avoid processing that
6307 // row more than once.
6308 let mut start_row = selection.start.row;
6309 if last_toggled_row == Some(start_row) {
6310 start_row += 1;
6311 }
6312 let end_row =
6313 if selection.end.row > selection.start.row && selection.end.column == 0 {
6314 selection.end.row - 1
6315 } else {
6316 selection.end.row
6317 };
6318 last_toggled_row = Some(end_row);
6319
6320 if start_row > end_row {
6321 continue;
6322 }
6323
6324 // If the language has line comments, toggle those.
6325 if let Some(full_comment_prefix) = language.line_comment_prefix() {
6326 // Split the comment prefix's trailing whitespace into a separate string,
6327 // as that portion won't be used for detecting if a line is a comment.
6328 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6329 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6330 let mut all_selection_lines_are_comments = true;
6331
6332 for row in start_row..=end_row {
6333 if snapshot.is_line_blank(row) && start_row < end_row {
6334 continue;
6335 }
6336
6337 let prefix_range = comment_prefix_range(
6338 snapshot.deref(),
6339 row,
6340 comment_prefix,
6341 comment_prefix_whitespace,
6342 );
6343 if prefix_range.is_empty() {
6344 all_selection_lines_are_comments = false;
6345 }
6346 selection_edit_ranges.push(prefix_range);
6347 }
6348
6349 if all_selection_lines_are_comments {
6350 edits.extend(
6351 selection_edit_ranges
6352 .iter()
6353 .cloned()
6354 .map(|range| (range, empty_str.clone())),
6355 );
6356 } else {
6357 let min_column = selection_edit_ranges
6358 .iter()
6359 .map(|r| r.start.column)
6360 .min()
6361 .unwrap_or(0);
6362 edits.extend(selection_edit_ranges.iter().map(|range| {
6363 let position = Point::new(range.start.row, min_column);
6364 (position..position, full_comment_prefix.clone())
6365 }));
6366 }
6367 } else if let Some((full_comment_prefix, comment_suffix)) =
6368 language.block_comment_delimiters()
6369 {
6370 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
6371 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
6372 let prefix_range = comment_prefix_range(
6373 snapshot.deref(),
6374 start_row,
6375 comment_prefix,
6376 comment_prefix_whitespace,
6377 );
6378 let suffix_range = comment_suffix_range(
6379 snapshot.deref(),
6380 end_row,
6381 comment_suffix.trim_start_matches(' '),
6382 comment_suffix.starts_with(' '),
6383 );
6384
6385 if prefix_range.is_empty() || suffix_range.is_empty() {
6386 edits.push((
6387 prefix_range.start..prefix_range.start,
6388 full_comment_prefix.clone(),
6389 ));
6390 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
6391 suffixes_inserted.push((end_row, comment_suffix.len()));
6392 } else {
6393 edits.push((prefix_range, empty_str.clone()));
6394 edits.push((suffix_range, empty_str.clone()));
6395 }
6396 } else {
6397 continue;
6398 }
6399 }
6400
6401 drop(snapshot);
6402 this.buffer.update(cx, |buffer, cx| {
6403 buffer.edit(edits, None, cx);
6404 });
6405
6406 // Adjust selections so that they end before any comment suffixes that
6407 // were inserted.
6408 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
6409 let mut selections = this.selections.all::<Point>(cx);
6410 let snapshot = this.buffer.read(cx).read(cx);
6411 for selection in &mut selections {
6412 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
6413 match row.cmp(&selection.end.row) {
6414 Ordering::Less => {
6415 suffixes_inserted.next();
6416 continue;
6417 }
6418 Ordering::Greater => break,
6419 Ordering::Equal => {
6420 if selection.end.column == snapshot.line_len(row) {
6421 if selection.is_empty() {
6422 selection.start.column -= suffix_len as u32;
6423 }
6424 selection.end.column -= suffix_len as u32;
6425 }
6426 break;
6427 }
6428 }
6429 }
6430 }
6431
6432 drop(snapshot);
6433 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
6434
6435 let selections = this.selections.all::<Point>(cx);
6436 let selections_on_single_row = selections.windows(2).all(|selections| {
6437 selections[0].start.row == selections[1].start.row
6438 && selections[0].end.row == selections[1].end.row
6439 && selections[0].start.row == selections[0].end.row
6440 });
6441 let selections_selecting = selections
6442 .iter()
6443 .any(|selection| selection.start != selection.end);
6444 let advance_downwards = action.advance_downwards
6445 && selections_on_single_row
6446 && !selections_selecting
6447 && this.mode != EditorMode::SingleLine;
6448
6449 if advance_downwards {
6450 let snapshot = this.buffer.read(cx).snapshot(cx);
6451
6452 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
6453 s.move_cursors_with(|display_snapshot, display_point, _| {
6454 let mut point = display_point.to_point(display_snapshot);
6455 point.row += 1;
6456 point = snapshot.clip_point(point, Bias::Left);
6457 let display_point = point.to_display_point(display_snapshot);
6458 (display_point, SelectionGoal::Column(display_point.column()))
6459 })
6460 });
6461 }
6462 });
6463 }
6464
6465 pub fn select_larger_syntax_node(
6466 &mut self,
6467 _: &SelectLargerSyntaxNode,
6468 cx: &mut ViewContext<Self>,
6469 ) {
6470 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6471 let buffer = self.buffer.read(cx).snapshot(cx);
6472 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
6473
6474 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6475 let mut selected_larger_node = false;
6476 let new_selections = old_selections
6477 .iter()
6478 .map(|selection| {
6479 let old_range = selection.start..selection.end;
6480 let mut new_range = old_range.clone();
6481 while let Some(containing_range) =
6482 buffer.range_for_syntax_ancestor(new_range.clone())
6483 {
6484 new_range = containing_range;
6485 if !display_map.intersects_fold(new_range.start)
6486 && !display_map.intersects_fold(new_range.end)
6487 {
6488 break;
6489 }
6490 }
6491
6492 selected_larger_node |= new_range != old_range;
6493 Selection {
6494 id: selection.id,
6495 start: new_range.start,
6496 end: new_range.end,
6497 goal: SelectionGoal::None,
6498 reversed: selection.reversed,
6499 }
6500 })
6501 .collect::<Vec<_>>();
6502
6503 if selected_larger_node {
6504 stack.push(old_selections);
6505 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6506 s.select(new_selections);
6507 });
6508 }
6509 self.select_larger_syntax_node_stack = stack;
6510 }
6511
6512 pub fn select_smaller_syntax_node(
6513 &mut self,
6514 _: &SelectSmallerSyntaxNode,
6515 cx: &mut ViewContext<Self>,
6516 ) {
6517 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
6518 if let Some(selections) = stack.pop() {
6519 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6520 s.select(selections.to_vec());
6521 });
6522 }
6523 self.select_larger_syntax_node_stack = stack;
6524 }
6525
6526 pub fn move_to_enclosing_bracket(
6527 &mut self,
6528 _: &MoveToEnclosingBracket,
6529 cx: &mut ViewContext<Self>,
6530 ) {
6531 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6532 s.move_offsets_with(|snapshot, selection| {
6533 let Some(enclosing_bracket_ranges) =
6534 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
6535 else {
6536 return;
6537 };
6538
6539 let mut best_length = usize::MAX;
6540 let mut best_inside = false;
6541 let mut best_in_bracket_range = false;
6542 let mut best_destination = None;
6543 for (open, close) in enclosing_bracket_ranges {
6544 let close = close.to_inclusive();
6545 let length = close.end() - open.start;
6546 let inside = selection.start >= open.end && selection.end <= *close.start();
6547 let in_bracket_range = open.to_inclusive().contains(&selection.head())
6548 || close.contains(&selection.head());
6549
6550 // If best is next to a bracket and current isn't, skip
6551 if !in_bracket_range && best_in_bracket_range {
6552 continue;
6553 }
6554
6555 // Prefer smaller lengths unless best is inside and current isn't
6556 if length > best_length && (best_inside || !inside) {
6557 continue;
6558 }
6559
6560 best_length = length;
6561 best_inside = inside;
6562 best_in_bracket_range = in_bracket_range;
6563 best_destination = Some(
6564 if close.contains(&selection.start) && close.contains(&selection.end) {
6565 if inside {
6566 open.end
6567 } else {
6568 open.start
6569 }
6570 } else {
6571 if inside {
6572 *close.start()
6573 } else {
6574 *close.end()
6575 }
6576 },
6577 );
6578 }
6579
6580 if let Some(destination) = best_destination {
6581 selection.collapse_to(destination, SelectionGoal::None);
6582 }
6583 })
6584 });
6585 }
6586
6587 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
6588 self.end_selection(cx);
6589 self.selection_history.mode = SelectionHistoryMode::Undoing;
6590 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
6591 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6592 self.select_next_state = entry.select_next_state;
6593 self.select_prev_state = entry.select_prev_state;
6594 self.add_selections_state = entry.add_selections_state;
6595 self.request_autoscroll(Autoscroll::newest(), cx);
6596 }
6597 self.selection_history.mode = SelectionHistoryMode::Normal;
6598 }
6599
6600 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
6601 self.end_selection(cx);
6602 self.selection_history.mode = SelectionHistoryMode::Redoing;
6603 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
6604 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
6605 self.select_next_state = entry.select_next_state;
6606 self.select_prev_state = entry.select_prev_state;
6607 self.add_selections_state = entry.add_selections_state;
6608 self.request_autoscroll(Autoscroll::newest(), cx);
6609 }
6610 self.selection_history.mode = SelectionHistoryMode::Normal;
6611 }
6612
6613 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
6614 self.go_to_diagnostic_impl(Direction::Next, cx)
6615 }
6616
6617 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6618 self.go_to_diagnostic_impl(Direction::Prev, cx)
6619 }
6620
6621 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6622 let buffer = self.buffer.read(cx).snapshot(cx);
6623 let selection = self.selections.newest::<usize>(cx);
6624
6625 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6626 if direction == Direction::Next {
6627 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6628 let (group_id, jump_to) = popover.activation_info();
6629 if self.activate_diagnostics(group_id, cx) {
6630 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6631 let mut new_selection = s.newest_anchor().clone();
6632 new_selection.collapse_to(jump_to, SelectionGoal::None);
6633 s.select_anchors(vec![new_selection.clone()]);
6634 });
6635 }
6636 return;
6637 }
6638 }
6639
6640 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6641 active_diagnostics
6642 .primary_range
6643 .to_offset(&buffer)
6644 .to_inclusive()
6645 });
6646 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6647 if active_primary_range.contains(&selection.head()) {
6648 *active_primary_range.end()
6649 } else {
6650 selection.head()
6651 }
6652 } else {
6653 selection.head()
6654 };
6655
6656 loop {
6657 let mut diagnostics = if direction == Direction::Prev {
6658 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6659 } else {
6660 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6661 };
6662 let group = diagnostics.find_map(|entry| {
6663 if entry.diagnostic.is_primary
6664 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6665 && !entry.range.is_empty()
6666 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6667 {
6668 Some((entry.range, entry.diagnostic.group_id))
6669 } else {
6670 None
6671 }
6672 });
6673
6674 if let Some((primary_range, group_id)) = group {
6675 if self.activate_diagnostics(group_id, cx) {
6676 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6677 s.select(vec![Selection {
6678 id: selection.id,
6679 start: primary_range.start,
6680 end: primary_range.start,
6681 reversed: false,
6682 goal: SelectionGoal::None,
6683 }]);
6684 });
6685 }
6686 break;
6687 } else {
6688 // Cycle around to the start of the buffer, potentially moving back to the start of
6689 // the currently active diagnostic.
6690 active_primary_range.take();
6691 if direction == Direction::Prev {
6692 if search_start == buffer.len() {
6693 break;
6694 } else {
6695 search_start = buffer.len();
6696 }
6697 } else if search_start == 0 {
6698 break;
6699 } else {
6700 search_start = 0;
6701 }
6702 }
6703 }
6704 }
6705
6706 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6707 let snapshot = self
6708 .display_map
6709 .update(cx, |display_map, cx| display_map.snapshot(cx));
6710 let selection = self.selections.newest::<Point>(cx);
6711
6712 if !self.seek_in_direction(
6713 &snapshot,
6714 selection.head(),
6715 false,
6716 snapshot
6717 .buffer_snapshot
6718 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6719 cx,
6720 ) {
6721 let wrapped_point = Point::zero();
6722 self.seek_in_direction(
6723 &snapshot,
6724 wrapped_point,
6725 true,
6726 snapshot
6727 .buffer_snapshot
6728 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6729 cx,
6730 );
6731 }
6732 }
6733
6734 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6735 let snapshot = self
6736 .display_map
6737 .update(cx, |display_map, cx| display_map.snapshot(cx));
6738 let selection = self.selections.newest::<Point>(cx);
6739
6740 if !self.seek_in_direction(
6741 &snapshot,
6742 selection.head(),
6743 false,
6744 snapshot
6745 .buffer_snapshot
6746 .git_diff_hunks_in_range_rev(0..selection.head().row),
6747 cx,
6748 ) {
6749 let wrapped_point = snapshot.buffer_snapshot.max_point();
6750 self.seek_in_direction(
6751 &snapshot,
6752 wrapped_point,
6753 true,
6754 snapshot
6755 .buffer_snapshot
6756 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6757 cx,
6758 );
6759 }
6760 }
6761
6762 fn seek_in_direction(
6763 &mut self,
6764 snapshot: &DisplaySnapshot,
6765 initial_point: Point,
6766 is_wrapped: bool,
6767 hunks: impl Iterator<Item = DiffHunk<u32>>,
6768 cx: &mut ViewContext<Editor>,
6769 ) -> bool {
6770 let display_point = initial_point.to_display_point(snapshot);
6771 let mut hunks = hunks
6772 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6773 .skip_while(|hunk| {
6774 if is_wrapped {
6775 false
6776 } else {
6777 hunk.contains_display_row(display_point.row())
6778 }
6779 })
6780 .dedup();
6781
6782 if let Some(hunk) = hunks.next() {
6783 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6784 let row = hunk.start_display_row();
6785 let point = DisplayPoint::new(row, 0);
6786 s.select_display_ranges([point..point]);
6787 });
6788
6789 true
6790 } else {
6791 false
6792 }
6793 }
6794
6795 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6796 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx);
6797 }
6798
6799 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6800 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx);
6801 }
6802
6803 pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
6804 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
6805 }
6806
6807 pub fn go_to_type_definition_split(
6808 &mut self,
6809 _: &GoToTypeDefinitionSplit,
6810 cx: &mut ViewContext<Self>,
6811 ) {
6812 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx);
6813 }
6814
6815 fn go_to_definition_of_kind(
6816 &mut self,
6817 kind: GotoDefinitionKind,
6818 split: bool,
6819 cx: &mut ViewContext<Self>,
6820 ) {
6821 let Some(workspace) = self.workspace(cx) else {
6822 return;
6823 };
6824 let buffer = self.buffer.read(cx);
6825 let head = self.selections.newest::<usize>(cx).head();
6826 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6827 text_anchor
6828 } else {
6829 return;
6830 };
6831
6832 let project = workspace.read(cx).project().clone();
6833 let definitions = project.update(cx, |project, cx| match kind {
6834 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6835 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6836 });
6837
6838 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6839 let definitions = definitions.await?;
6840 editor.update(&mut cx, |editor, cx| {
6841 editor.navigate_to_definitions(
6842 definitions
6843 .into_iter()
6844 .map(GoToDefinitionLink::Text)
6845 .collect(),
6846 split,
6847 cx,
6848 );
6849 })?;
6850 Ok::<(), anyhow::Error>(())
6851 })
6852 .detach_and_log_err(cx);
6853 }
6854
6855 pub fn navigate_to_definitions(
6856 &mut self,
6857 mut definitions: Vec<GoToDefinitionLink>,
6858 split: bool,
6859 cx: &mut ViewContext<Editor>,
6860 ) {
6861 let Some(workspace) = self.workspace(cx) else {
6862 return;
6863 };
6864 let pane = workspace.read(cx).active_pane().clone();
6865 // If there is one definition, just open it directly
6866 if definitions.len() == 1 {
6867 let definition = definitions.pop().unwrap();
6868 let target_task = match definition {
6869 GoToDefinitionLink::Text(link) => Task::Ready(Some(Ok(Some(link.target)))),
6870 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6871 self.compute_target_location(lsp_location, server_id, cx)
6872 }
6873 };
6874 cx.spawn(|editor, mut cx| async move {
6875 let target = target_task.await.context("target resolution task")?;
6876 if let Some(target) = target {
6877 editor.update(&mut cx, |editor, cx| {
6878 let range = target.range.to_offset(target.buffer.read(cx));
6879 let range = editor.range_for_match(&range);
6880 if Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
6881 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6882 s.select_ranges([range]);
6883 });
6884 } else {
6885 cx.window_context().defer(move |cx| {
6886 let target_editor: ViewHandle<Self> =
6887 workspace.update(cx, |workspace, cx| {
6888 if split {
6889 workspace.split_project_item(target.buffer.clone(), cx)
6890 } else {
6891 workspace.open_project_item(target.buffer.clone(), cx)
6892 }
6893 });
6894 target_editor.update(cx, |target_editor, cx| {
6895 // When selecting a definition in a different buffer, disable the nav history
6896 // to avoid creating a history entry at the previous cursor location.
6897 pane.update(cx, |pane, _| pane.disable_history());
6898 target_editor.change_selections(
6899 Some(Autoscroll::fit()),
6900 cx,
6901 |s| {
6902 s.select_ranges([range]);
6903 },
6904 );
6905 pane.update(cx, |pane, _| pane.enable_history());
6906 });
6907 });
6908 }
6909 })
6910 } else {
6911 Ok(())
6912 }
6913 })
6914 .detach_and_log_err(cx);
6915 } else if !definitions.is_empty() {
6916 let replica_id = self.replica_id(cx);
6917 cx.spawn(|editor, mut cx| async move {
6918 let (title, location_tasks) = editor
6919 .update(&mut cx, |editor, cx| {
6920 let title = definitions
6921 .iter()
6922 .find_map(|definition| match definition {
6923 GoToDefinitionLink::Text(link) => {
6924 link.origin.as_ref().map(|origin| {
6925 let buffer = origin.buffer.read(cx);
6926 format!(
6927 "Definitions for {}",
6928 buffer
6929 .text_for_range(origin.range.clone())
6930 .collect::<String>()
6931 )
6932 })
6933 }
6934 GoToDefinitionLink::InlayHint(_, _) => None,
6935 })
6936 .unwrap_or("Definitions".to_string());
6937 let location_tasks = definitions
6938 .into_iter()
6939 .map(|definition| match definition {
6940 GoToDefinitionLink::Text(link) => {
6941 Task::Ready(Some(Ok(Some(link.target))))
6942 }
6943 GoToDefinitionLink::InlayHint(lsp_location, server_id) => {
6944 editor.compute_target_location(lsp_location, server_id, cx)
6945 }
6946 })
6947 .collect::<Vec<_>>();
6948 (title, location_tasks)
6949 })
6950 .context("location tasks preparation")?;
6951
6952 let locations = futures::future::join_all(location_tasks)
6953 .await
6954 .into_iter()
6955 .filter_map(|location| location.transpose())
6956 .collect::<Result<_>>()
6957 .context("location tasks")?;
6958 workspace.update(&mut cx, |workspace, cx| {
6959 Self::open_locations_in_multibuffer(
6960 workspace, locations, replica_id, title, split, cx,
6961 )
6962 });
6963
6964 anyhow::Ok(())
6965 })
6966 .detach_and_log_err(cx);
6967 }
6968 }
6969
6970 fn compute_target_location(
6971 &self,
6972 lsp_location: lsp::Location,
6973 server_id: LanguageServerId,
6974 cx: &mut ViewContext<Editor>,
6975 ) -> Task<anyhow::Result<Option<Location>>> {
6976 let Some(project) = self.project.clone() else {
6977 return Task::Ready(Some(Ok(None)));
6978 };
6979
6980 cx.spawn(move |editor, mut cx| async move {
6981 let location_task = editor.update(&mut cx, |editor, cx| {
6982 project.update(cx, |project, cx| {
6983 let language_server_name =
6984 editor.buffer.read(cx).as_singleton().and_then(|buffer| {
6985 project
6986 .language_server_for_buffer(buffer.read(cx), server_id, cx)
6987 .map(|(_, lsp_adapter)| {
6988 LanguageServerName(Arc::from(lsp_adapter.name()))
6989 })
6990 });
6991 language_server_name.map(|language_server_name| {
6992 project.open_local_buffer_via_lsp(
6993 lsp_location.uri.clone(),
6994 server_id,
6995 language_server_name,
6996 cx,
6997 )
6998 })
6999 })
7000 })?;
7001 let location = match location_task {
7002 Some(task) => Some({
7003 let target_buffer_handle = task.await.context("open local buffer")?;
7004 let range = {
7005 target_buffer_handle.update(&mut cx, |target_buffer, _| {
7006 let target_start = target_buffer.clip_point_utf16(
7007 point_from_lsp(lsp_location.range.start),
7008 Bias::Left,
7009 );
7010 let target_end = target_buffer.clip_point_utf16(
7011 point_from_lsp(lsp_location.range.end),
7012 Bias::Left,
7013 );
7014 target_buffer.anchor_after(target_start)
7015 ..target_buffer.anchor_before(target_end)
7016 })
7017 };
7018 Location {
7019 buffer: target_buffer_handle,
7020 range,
7021 }
7022 }),
7023 None => None,
7024 };
7025 Ok(location)
7026 })
7027 }
7028
7029 pub fn find_all_references(
7030 workspace: &mut Workspace,
7031 _: &FindAllReferences,
7032 cx: &mut ViewContext<Workspace>,
7033 ) -> Option<Task<Result<()>>> {
7034 let active_item = workspace.active_item(cx)?;
7035 let editor_handle = active_item.act_as::<Self>(cx)?;
7036
7037 let editor = editor_handle.read(cx);
7038 let buffer = editor.buffer.read(cx);
7039 let head = editor.selections.newest::<usize>(cx).head();
7040 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
7041 let replica_id = editor.replica_id(cx);
7042
7043 let project = workspace.project().clone();
7044 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
7045 Some(cx.spawn_labeled(
7046 "Finding All References...",
7047 |workspace, mut cx| async move {
7048 let locations = references.await?;
7049 if locations.is_empty() {
7050 return Ok(());
7051 }
7052
7053 workspace.update(&mut cx, |workspace, cx| {
7054 let title = locations
7055 .first()
7056 .as_ref()
7057 .map(|location| {
7058 let buffer = location.buffer.read(cx);
7059 format!(
7060 "References to `{}`",
7061 buffer
7062 .text_for_range(location.range.clone())
7063 .collect::<String>()
7064 )
7065 })
7066 .unwrap();
7067 Self::open_locations_in_multibuffer(
7068 workspace, locations, replica_id, title, false, cx,
7069 );
7070 })?;
7071
7072 Ok(())
7073 },
7074 ))
7075 }
7076
7077 /// Opens a multibuffer with the given project locations in it
7078 pub fn open_locations_in_multibuffer(
7079 workspace: &mut Workspace,
7080 mut locations: Vec<Location>,
7081 replica_id: ReplicaId,
7082 title: String,
7083 split: bool,
7084 cx: &mut ViewContext<Workspace>,
7085 ) {
7086 // If there are multiple definitions, open them in a multibuffer
7087 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
7088 let mut locations = locations.into_iter().peekable();
7089 let mut ranges_to_highlight = Vec::new();
7090
7091 let excerpt_buffer = cx.add_model(|cx| {
7092 let mut multibuffer = MultiBuffer::new(replica_id);
7093 while let Some(location) = locations.next() {
7094 let buffer = location.buffer.read(cx);
7095 let mut ranges_for_buffer = Vec::new();
7096 let range = location.range.to_offset(buffer);
7097 ranges_for_buffer.push(range.clone());
7098
7099 while let Some(next_location) = locations.peek() {
7100 if next_location.buffer == location.buffer {
7101 ranges_for_buffer.push(next_location.range.to_offset(buffer));
7102 locations.next();
7103 } else {
7104 break;
7105 }
7106 }
7107
7108 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
7109 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
7110 location.buffer.clone(),
7111 ranges_for_buffer,
7112 1,
7113 cx,
7114 ))
7115 }
7116
7117 multibuffer.with_title(title)
7118 });
7119
7120 let editor = cx.add_view(|cx| {
7121 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
7122 });
7123 editor.update(cx, |editor, cx| {
7124 editor.highlight_background::<Self>(
7125 ranges_to_highlight,
7126 |theme| theme.editor.highlighted_line_background,
7127 cx,
7128 );
7129 });
7130 if split {
7131 workspace.split_item(Box::new(editor), cx);
7132 } else {
7133 workspace.add_item(Box::new(editor), cx);
7134 }
7135 }
7136
7137 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7138 use language::ToOffset as _;
7139
7140 let project = self.project.clone()?;
7141 let selection = self.selections.newest_anchor().clone();
7142 let (cursor_buffer, cursor_buffer_position) = self
7143 .buffer
7144 .read(cx)
7145 .text_anchor_for_position(selection.head(), cx)?;
7146 let (tail_buffer, _) = self
7147 .buffer
7148 .read(cx)
7149 .text_anchor_for_position(selection.tail(), cx)?;
7150 if tail_buffer != cursor_buffer {
7151 return None;
7152 }
7153
7154 let snapshot = cursor_buffer.read(cx).snapshot();
7155 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
7156 let prepare_rename = project.update(cx, |project, cx| {
7157 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
7158 });
7159
7160 Some(cx.spawn(|this, mut cx| async move {
7161 let rename_range = if let Some(range) = prepare_rename.await? {
7162 Some(range)
7163 } else {
7164 this.update(&mut cx, |this, cx| {
7165 let buffer = this.buffer.read(cx).snapshot(cx);
7166 let mut buffer_highlights = this
7167 .document_highlights_for_position(selection.head(), &buffer)
7168 .filter(|highlight| {
7169 highlight.start.excerpt_id() == selection.head().excerpt_id()
7170 && highlight.end.excerpt_id() == selection.head().excerpt_id()
7171 });
7172 buffer_highlights
7173 .next()
7174 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
7175 })?
7176 };
7177 if let Some(rename_range) = rename_range {
7178 let rename_buffer_range = rename_range.to_offset(&snapshot);
7179 let cursor_offset_in_rename_range =
7180 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
7181
7182 this.update(&mut cx, |this, cx| {
7183 this.take_rename(false, cx);
7184 let style = this.style(cx);
7185 let buffer = this.buffer.read(cx).read(cx);
7186 let cursor_offset = selection.head().to_offset(&buffer);
7187 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
7188 let rename_end = rename_start + rename_buffer_range.len();
7189 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
7190 let mut old_highlight_id = None;
7191 let old_name: Arc<str> = buffer
7192 .chunks(rename_start..rename_end, true)
7193 .map(|chunk| {
7194 if old_highlight_id.is_none() {
7195 old_highlight_id = chunk.syntax_highlight_id;
7196 }
7197 chunk.text
7198 })
7199 .collect::<String>()
7200 .into();
7201
7202 drop(buffer);
7203
7204 // Position the selection in the rename editor so that it matches the current selection.
7205 this.show_local_selections = false;
7206 let rename_editor = cx.add_view(|cx| {
7207 let mut editor = Editor::single_line(None, cx);
7208 if let Some(old_highlight_id) = old_highlight_id {
7209 editor.override_text_style =
7210 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
7211 }
7212 editor.buffer.update(cx, |buffer, cx| {
7213 buffer.edit([(0..0, old_name.clone())], None, cx)
7214 });
7215 editor.select_all(&SelectAll, cx);
7216 editor
7217 });
7218
7219 let ranges = this
7220 .clear_background_highlights::<DocumentHighlightWrite>(cx)
7221 .into_iter()
7222 .flat_map(|(_, ranges)| ranges.into_iter())
7223 .chain(
7224 this.clear_background_highlights::<DocumentHighlightRead>(cx)
7225 .into_iter()
7226 .flat_map(|(_, ranges)| ranges.into_iter()),
7227 )
7228 .collect();
7229
7230 this.highlight_text::<Rename>(
7231 ranges,
7232 HighlightStyle {
7233 fade_out: Some(style.rename_fade),
7234 ..Default::default()
7235 },
7236 cx,
7237 );
7238 cx.focus(&rename_editor);
7239 let block_id = this.insert_blocks(
7240 [BlockProperties {
7241 style: BlockStyle::Flex,
7242 position: range.start.clone(),
7243 height: 1,
7244 render: Arc::new({
7245 let editor = rename_editor.clone();
7246 move |cx: &mut BlockContext| {
7247 ChildView::new(&editor, cx)
7248 .contained()
7249 .with_padding_left(cx.anchor_x)
7250 .into_any()
7251 }
7252 }),
7253 disposition: BlockDisposition::Below,
7254 }],
7255 Some(Autoscroll::fit()),
7256 cx,
7257 )[0];
7258 this.pending_rename = Some(RenameState {
7259 range,
7260 old_name,
7261 editor: rename_editor,
7262 block_id,
7263 });
7264 })?;
7265 }
7266
7267 Ok(())
7268 }))
7269 }
7270
7271 pub fn confirm_rename(
7272 workspace: &mut Workspace,
7273 _: &ConfirmRename,
7274 cx: &mut ViewContext<Workspace>,
7275 ) -> Option<Task<Result<()>>> {
7276 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
7277
7278 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
7279 let rename = editor.take_rename(false, cx)?;
7280 let buffer = editor.buffer.read(cx);
7281 let (start_buffer, start) =
7282 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
7283 let (end_buffer, end) =
7284 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
7285 if start_buffer == end_buffer {
7286 let new_name = rename.editor.read(cx).text(cx);
7287 Some((start_buffer, start..end, rename.old_name, new_name))
7288 } else {
7289 None
7290 }
7291 })?;
7292
7293 let rename = workspace.project().clone().update(cx, |project, cx| {
7294 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
7295 });
7296
7297 let editor = editor.downgrade();
7298 Some(cx.spawn(|workspace, mut cx| async move {
7299 let project_transaction = rename.await?;
7300 Self::open_project_transaction(
7301 &editor,
7302 workspace,
7303 project_transaction,
7304 format!("Rename: {} → {}", old_name, new_name),
7305 cx.clone(),
7306 )
7307 .await?;
7308
7309 editor.update(&mut cx, |editor, cx| {
7310 editor.refresh_document_highlights(cx);
7311 })?;
7312 Ok(())
7313 }))
7314 }
7315
7316 fn take_rename(
7317 &mut self,
7318 moving_cursor: bool,
7319 cx: &mut ViewContext<Self>,
7320 ) -> Option<RenameState> {
7321 let rename = self.pending_rename.take()?;
7322 self.remove_blocks(
7323 [rename.block_id].into_iter().collect(),
7324 Some(Autoscroll::fit()),
7325 cx,
7326 );
7327 self.clear_highlights::<Rename>(cx);
7328 self.show_local_selections = true;
7329
7330 if moving_cursor {
7331 let rename_editor = rename.editor.read(cx);
7332 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
7333
7334 // Update the selection to match the position of the selection inside
7335 // the rename editor.
7336 let snapshot = self.buffer.read(cx).read(cx);
7337 let rename_range = rename.range.to_offset(&snapshot);
7338 let cursor_in_editor = snapshot
7339 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
7340 .min(rename_range.end);
7341 drop(snapshot);
7342
7343 self.change_selections(None, cx, |s| {
7344 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
7345 });
7346 } else {
7347 self.refresh_document_highlights(cx);
7348 }
7349
7350 Some(rename)
7351 }
7352
7353 #[cfg(any(test, feature = "test-support"))]
7354 pub fn pending_rename(&self) -> Option<&RenameState> {
7355 self.pending_rename.as_ref()
7356 }
7357
7358 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
7359 let project = match &self.project {
7360 Some(project) => project.clone(),
7361 None => return None,
7362 };
7363
7364 Some(self.perform_format(project, FormatTrigger::Manual, cx))
7365 }
7366
7367 fn perform_format(
7368 &mut self,
7369 project: ModelHandle<Project>,
7370 trigger: FormatTrigger,
7371 cx: &mut ViewContext<Self>,
7372 ) -> Task<Result<()>> {
7373 let buffer = self.buffer().clone();
7374 let buffers = buffer.read(cx).all_buffers();
7375
7376 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
7377 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
7378
7379 cx.spawn(|_, mut cx| async move {
7380 let transaction = futures::select_biased! {
7381 _ = timeout => {
7382 log::warn!("timed out waiting for formatting");
7383 None
7384 }
7385 transaction = format.log_err().fuse() => transaction,
7386 };
7387
7388 buffer.update(&mut cx, |buffer, cx| {
7389 if let Some(transaction) = transaction {
7390 if !buffer.is_singleton() {
7391 buffer.push_transaction(&transaction.0, cx);
7392 }
7393 }
7394
7395 cx.notify();
7396 });
7397
7398 Ok(())
7399 })
7400 }
7401
7402 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
7403 if let Some(project) = self.project.clone() {
7404 self.buffer.update(cx, |multi_buffer, cx| {
7405 project.update(cx, |project, cx| {
7406 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
7407 });
7408 })
7409 }
7410 }
7411
7412 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
7413 cx.show_character_palette();
7414 }
7415
7416 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
7417 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
7418 let buffer = self.buffer.read(cx).snapshot(cx);
7419 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
7420 let is_valid = buffer
7421 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
7422 .any(|entry| {
7423 entry.diagnostic.is_primary
7424 && !entry.range.is_empty()
7425 && entry.range.start == primary_range_start
7426 && entry.diagnostic.message == active_diagnostics.primary_message
7427 });
7428
7429 if is_valid != active_diagnostics.is_valid {
7430 active_diagnostics.is_valid = is_valid;
7431 let mut new_styles = HashMap::default();
7432 for (block_id, diagnostic) in &active_diagnostics.blocks {
7433 new_styles.insert(
7434 *block_id,
7435 diagnostic_block_renderer(diagnostic.clone(), is_valid),
7436 );
7437 }
7438 self.display_map
7439 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
7440 }
7441 }
7442 }
7443
7444 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
7445 self.dismiss_diagnostics(cx);
7446 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
7447 let buffer = self.buffer.read(cx).snapshot(cx);
7448
7449 let mut primary_range = None;
7450 let mut primary_message = None;
7451 let mut group_end = Point::zero();
7452 let diagnostic_group = buffer
7453 .diagnostic_group::<Point>(group_id)
7454 .map(|entry| {
7455 if entry.range.end > group_end {
7456 group_end = entry.range.end;
7457 }
7458 if entry.diagnostic.is_primary {
7459 primary_range = Some(entry.range.clone());
7460 primary_message = Some(entry.diagnostic.message.clone());
7461 }
7462 entry
7463 })
7464 .collect::<Vec<_>>();
7465 let primary_range = primary_range?;
7466 let primary_message = primary_message?;
7467 let primary_range =
7468 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
7469
7470 let blocks = display_map
7471 .insert_blocks(
7472 diagnostic_group.iter().map(|entry| {
7473 let diagnostic = entry.diagnostic.clone();
7474 let message_height = diagnostic.message.lines().count() as u8;
7475 BlockProperties {
7476 style: BlockStyle::Fixed,
7477 position: buffer.anchor_after(entry.range.start),
7478 height: message_height,
7479 render: diagnostic_block_renderer(diagnostic, true),
7480 disposition: BlockDisposition::Below,
7481 }
7482 }),
7483 cx,
7484 )
7485 .into_iter()
7486 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
7487 .collect();
7488
7489 Some(ActiveDiagnosticGroup {
7490 primary_range,
7491 primary_message,
7492 blocks,
7493 is_valid: true,
7494 })
7495 });
7496 self.active_diagnostics.is_some()
7497 }
7498
7499 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
7500 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
7501 self.display_map.update(cx, |display_map, cx| {
7502 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
7503 });
7504 cx.notify();
7505 }
7506 }
7507
7508 pub fn set_selections_from_remote(
7509 &mut self,
7510 selections: Vec<Selection<Anchor>>,
7511 pending_selection: Option<Selection<Anchor>>,
7512 cx: &mut ViewContext<Self>,
7513 ) {
7514 let old_cursor_position = self.selections.newest_anchor().head();
7515 self.selections.change_with(cx, |s| {
7516 s.select_anchors(selections);
7517 if let Some(pending_selection) = pending_selection {
7518 s.set_pending(pending_selection, SelectMode::Character);
7519 } else {
7520 s.clear_pending();
7521 }
7522 });
7523 self.selections_did_change(false, &old_cursor_position, cx);
7524 }
7525
7526 fn push_to_selection_history(&mut self) {
7527 self.selection_history.push(SelectionHistoryEntry {
7528 selections: self.selections.disjoint_anchors(),
7529 select_next_state: self.select_next_state.clone(),
7530 select_prev_state: self.select_prev_state.clone(),
7531 add_selections_state: self.add_selections_state.clone(),
7532 });
7533 }
7534
7535 pub fn transact(
7536 &mut self,
7537 cx: &mut ViewContext<Self>,
7538 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
7539 ) -> Option<TransactionId> {
7540 self.start_transaction_at(Instant::now(), cx);
7541 update(self, cx);
7542 self.end_transaction_at(Instant::now(), cx)
7543 }
7544
7545 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
7546 self.end_selection(cx);
7547 if let Some(tx_id) = self
7548 .buffer
7549 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
7550 {
7551 self.selection_history
7552 .insert_transaction(tx_id, self.selections.disjoint_anchors());
7553 }
7554 }
7555
7556 fn end_transaction_at(
7557 &mut self,
7558 now: Instant,
7559 cx: &mut ViewContext<Self>,
7560 ) -> Option<TransactionId> {
7561 if let Some(tx_id) = self
7562 .buffer
7563 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
7564 {
7565 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
7566 *end_selections = Some(self.selections.disjoint_anchors());
7567 } else {
7568 error!("unexpectedly ended a transaction that wasn't started by this editor");
7569 }
7570
7571 cx.emit(Event::Edited);
7572 Some(tx_id)
7573 } else {
7574 None
7575 }
7576 }
7577
7578 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
7579 let mut fold_ranges = Vec::new();
7580
7581 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7582
7583 let selections = self.selections.all_adjusted(cx);
7584 for selection in selections {
7585 let range = selection.range().sorted();
7586 let buffer_start_row = range.start.row;
7587
7588 for row in (0..=range.end.row).rev() {
7589 let fold_range = display_map.foldable_range(row);
7590
7591 if let Some(fold_range) = fold_range {
7592 if fold_range.end.row >= buffer_start_row {
7593 fold_ranges.push(fold_range);
7594 if row <= range.start.row {
7595 break;
7596 }
7597 }
7598 }
7599 }
7600 }
7601
7602 self.fold_ranges(fold_ranges, true, cx);
7603 }
7604
7605 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
7606 let buffer_row = fold_at.buffer_row;
7607 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7608
7609 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
7610 let autoscroll = self
7611 .selections
7612 .all::<Point>(cx)
7613 .iter()
7614 .any(|selection| fold_range.overlaps(&selection.range()));
7615
7616 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
7617 }
7618 }
7619
7620 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
7621 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7622 let buffer = &display_map.buffer_snapshot;
7623 let selections = self.selections.all::<Point>(cx);
7624 let ranges = selections
7625 .iter()
7626 .map(|s| {
7627 let range = s.display_range(&display_map).sorted();
7628 let mut start = range.start.to_point(&display_map);
7629 let mut end = range.end.to_point(&display_map);
7630 start.column = 0;
7631 end.column = buffer.line_len(end.row);
7632 start..end
7633 })
7634 .collect::<Vec<_>>();
7635
7636 self.unfold_ranges(ranges, true, true, cx);
7637 }
7638
7639 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
7640 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7641
7642 let intersection_range = Point::new(unfold_at.buffer_row, 0)
7643 ..Point::new(
7644 unfold_at.buffer_row,
7645 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
7646 );
7647
7648 let autoscroll = self
7649 .selections
7650 .all::<Point>(cx)
7651 .iter()
7652 .any(|selection| selection.range().overlaps(&intersection_range));
7653
7654 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
7655 }
7656
7657 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
7658 let selections = self.selections.all::<Point>(cx);
7659 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7660 let line_mode = self.selections.line_mode;
7661 let ranges = selections.into_iter().map(|s| {
7662 if line_mode {
7663 let start = Point::new(s.start.row, 0);
7664 let end = Point::new(s.end.row, display_map.buffer_snapshot.line_len(s.end.row));
7665 start..end
7666 } else {
7667 s.start..s.end
7668 }
7669 });
7670 self.fold_ranges(ranges, true, cx);
7671 }
7672
7673 pub fn fold_ranges<T: ToOffset + Clone>(
7674 &mut self,
7675 ranges: impl IntoIterator<Item = Range<T>>,
7676 auto_scroll: bool,
7677 cx: &mut ViewContext<Self>,
7678 ) {
7679 let mut ranges = ranges.into_iter().peekable();
7680 if ranges.peek().is_some() {
7681 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
7682
7683 if auto_scroll {
7684 self.request_autoscroll(Autoscroll::fit(), cx);
7685 }
7686
7687 cx.notify();
7688 }
7689 }
7690
7691 pub fn unfold_ranges<T: ToOffset + Clone>(
7692 &mut self,
7693 ranges: impl IntoIterator<Item = Range<T>>,
7694 inclusive: bool,
7695 auto_scroll: bool,
7696 cx: &mut ViewContext<Self>,
7697 ) {
7698 let mut ranges = ranges.into_iter().peekable();
7699 if ranges.peek().is_some() {
7700 self.display_map
7701 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
7702 if auto_scroll {
7703 self.request_autoscroll(Autoscroll::fit(), cx);
7704 }
7705
7706 cx.notify();
7707 }
7708 }
7709
7710 pub fn gutter_hover(
7711 &mut self,
7712 GutterHover { hovered }: &GutterHover,
7713 cx: &mut ViewContext<Self>,
7714 ) {
7715 self.gutter_hovered = *hovered;
7716 cx.notify();
7717 }
7718
7719 pub fn insert_blocks(
7720 &mut self,
7721 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
7722 autoscroll: Option<Autoscroll>,
7723 cx: &mut ViewContext<Self>,
7724 ) -> Vec<BlockId> {
7725 let blocks = self
7726 .display_map
7727 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
7728 if let Some(autoscroll) = autoscroll {
7729 self.request_autoscroll(autoscroll, cx);
7730 }
7731 blocks
7732 }
7733
7734 pub fn replace_blocks(
7735 &mut self,
7736 blocks: HashMap<BlockId, RenderBlock>,
7737 autoscroll: Option<Autoscroll>,
7738 cx: &mut ViewContext<Self>,
7739 ) {
7740 self.display_map
7741 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
7742 if let Some(autoscroll) = autoscroll {
7743 self.request_autoscroll(autoscroll, cx);
7744 }
7745 }
7746
7747 pub fn remove_blocks(
7748 &mut self,
7749 block_ids: HashSet<BlockId>,
7750 autoscroll: Option<Autoscroll>,
7751 cx: &mut ViewContext<Self>,
7752 ) {
7753 self.display_map.update(cx, |display_map, cx| {
7754 display_map.remove_blocks(block_ids, cx)
7755 });
7756 if let Some(autoscroll) = autoscroll {
7757 self.request_autoscroll(autoscroll, cx);
7758 }
7759 }
7760
7761 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
7762 self.display_map
7763 .update(cx, |map, cx| map.snapshot(cx))
7764 .longest_row()
7765 }
7766
7767 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7768 self.display_map
7769 .update(cx, |map, cx| map.snapshot(cx))
7770 .max_point()
7771 }
7772
7773 pub fn text(&self, cx: &AppContext) -> String {
7774 self.buffer.read(cx).read(cx).text()
7775 }
7776
7777 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7778 self.transact(cx, |this, cx| {
7779 this.buffer
7780 .read(cx)
7781 .as_singleton()
7782 .expect("you can only call set_text on editors for singleton buffers")
7783 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7784 });
7785 }
7786
7787 pub fn display_text(&self, cx: &mut AppContext) -> String {
7788 self.display_map
7789 .update(cx, |map, cx| map.snapshot(cx))
7790 .text()
7791 }
7792
7793 pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
7794 let mut wrap_guides = smallvec::smallvec![];
7795
7796 if self.show_wrap_guides == Some(false) {
7797 return wrap_guides;
7798 }
7799
7800 let settings = self.buffer.read(cx).settings_at(0, cx);
7801 if settings.show_wrap_guides {
7802 if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
7803 wrap_guides.push((soft_wrap as usize, true));
7804 }
7805 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
7806 }
7807
7808 wrap_guides
7809 }
7810
7811 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7812 let settings = self.buffer.read(cx).settings_at(0, cx);
7813 let mode = self
7814 .soft_wrap_mode_override
7815 .unwrap_or_else(|| settings.soft_wrap);
7816 match mode {
7817 language_settings::SoftWrap::None => SoftWrap::None,
7818 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7819 language_settings::SoftWrap::PreferredLineLength => {
7820 SoftWrap::Column(settings.preferred_line_length)
7821 }
7822 }
7823 }
7824
7825 pub fn set_soft_wrap_mode(
7826 &mut self,
7827 mode: language_settings::SoftWrap,
7828 cx: &mut ViewContext<Self>,
7829 ) {
7830 self.soft_wrap_mode_override = Some(mode);
7831 cx.notify();
7832 }
7833
7834 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7835 self.display_map
7836 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7837 }
7838
7839 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7840 if self.soft_wrap_mode_override.is_some() {
7841 self.soft_wrap_mode_override.take();
7842 } else {
7843 let soft_wrap = match self.soft_wrap_mode(cx) {
7844 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7845 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7846 };
7847 self.soft_wrap_mode_override = Some(soft_wrap);
7848 }
7849 cx.notify();
7850 }
7851
7852 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7853 self.show_gutter = show_gutter;
7854 cx.notify();
7855 }
7856
7857 pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7858 self.show_wrap_guides = Some(show_gutter);
7859 cx.notify();
7860 }
7861
7862 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7863 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7864 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7865 cx.reveal_path(&file.abs_path(cx));
7866 }
7867 }
7868 }
7869
7870 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7871 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7872 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7873 if let Some(path) = file.abs_path(cx).to_str() {
7874 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7875 }
7876 }
7877 }
7878 }
7879
7880 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7881 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7882 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7883 if let Some(path) = file.path().to_str() {
7884 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7885 }
7886 }
7887 }
7888 }
7889
7890 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7891 self.highlighted_rows = rows;
7892 }
7893
7894 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7895 self.highlighted_rows.clone()
7896 }
7897
7898 pub fn highlight_background<T: 'static>(
7899 &mut self,
7900 ranges: Vec<Range<Anchor>>,
7901 color_fetcher: fn(&Theme) -> Color,
7902 cx: &mut ViewContext<Self>,
7903 ) {
7904 self.background_highlights
7905 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7906 cx.notify();
7907 }
7908
7909 pub fn highlight_inlay_background<T: 'static>(
7910 &mut self,
7911 ranges: Vec<InlayHighlight>,
7912 color_fetcher: fn(&Theme) -> Color,
7913 cx: &mut ViewContext<Self>,
7914 ) {
7915 // TODO: no actual highlights happen for inlays currently, find a way to do that
7916 self.inlay_background_highlights
7917 .insert(Some(TypeId::of::<T>()), (color_fetcher, ranges));
7918 cx.notify();
7919 }
7920
7921 pub fn clear_background_highlights<T: 'static>(
7922 &mut self,
7923 cx: &mut ViewContext<Self>,
7924 ) -> Option<BackgroundHighlight> {
7925 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>());
7926 let inlay_highlights = self
7927 .inlay_background_highlights
7928 .remove(&Some(TypeId::of::<T>()));
7929 if text_highlights.is_some() || inlay_highlights.is_some() {
7930 cx.notify();
7931 }
7932 text_highlights
7933 }
7934
7935 #[cfg(feature = "test-support")]
7936 pub fn all_text_background_highlights(
7937 &mut self,
7938 cx: &mut ViewContext<Self>,
7939 ) -> Vec<(Range<DisplayPoint>, Color)> {
7940 let snapshot = self.snapshot(cx);
7941 let buffer = &snapshot.buffer_snapshot;
7942 let start = buffer.anchor_before(0);
7943 let end = buffer.anchor_after(buffer.len());
7944 let theme = theme::current(cx);
7945 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7946 }
7947
7948 fn document_highlights_for_position<'a>(
7949 &'a self,
7950 position: Anchor,
7951 buffer: &'a MultiBufferSnapshot,
7952 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7953 let read_highlights = self
7954 .background_highlights
7955 .get(&TypeId::of::<DocumentHighlightRead>())
7956 .map(|h| &h.1);
7957 let write_highlights = self
7958 .background_highlights
7959 .get(&TypeId::of::<DocumentHighlightWrite>())
7960 .map(|h| &h.1);
7961 let left_position = position.bias_left(buffer);
7962 let right_position = position.bias_right(buffer);
7963 read_highlights
7964 .into_iter()
7965 .chain(write_highlights)
7966 .flat_map(move |ranges| {
7967 let start_ix = match ranges.binary_search_by(|probe| {
7968 let cmp = probe.end.cmp(&left_position, buffer);
7969 if cmp.is_ge() {
7970 Ordering::Greater
7971 } else {
7972 Ordering::Less
7973 }
7974 }) {
7975 Ok(i) | Err(i) => i,
7976 };
7977
7978 let right_position = right_position.clone();
7979 ranges[start_ix..]
7980 .iter()
7981 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7982 })
7983 }
7984
7985 pub fn background_highlights_in_range(
7986 &self,
7987 search_range: Range<Anchor>,
7988 display_snapshot: &DisplaySnapshot,
7989 theme: &Theme,
7990 ) -> Vec<(Range<DisplayPoint>, Color)> {
7991 let mut results = Vec::new();
7992 for (color_fetcher, ranges) in self.background_highlights.values() {
7993 let color = color_fetcher(theme);
7994 let start_ix = match ranges.binary_search_by(|probe| {
7995 let cmp = probe
7996 .end
7997 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
7998 if cmp.is_gt() {
7999 Ordering::Greater
8000 } else {
8001 Ordering::Less
8002 }
8003 }) {
8004 Ok(i) | Err(i) => i,
8005 };
8006 for range in &ranges[start_ix..] {
8007 if range
8008 .start
8009 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8010 .is_ge()
8011 {
8012 break;
8013 }
8014
8015 let start = range.start.to_display_point(&display_snapshot);
8016 let end = range.end.to_display_point(&display_snapshot);
8017 results.push((start..end, color))
8018 }
8019 }
8020 results
8021 }
8022
8023 pub fn background_highlight_row_ranges<T: 'static>(
8024 &self,
8025 search_range: Range<Anchor>,
8026 display_snapshot: &DisplaySnapshot,
8027 count: usize,
8028 ) -> Vec<RangeInclusive<DisplayPoint>> {
8029 let mut results = Vec::new();
8030 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
8031 return vec![];
8032 };
8033
8034 let start_ix = match ranges.binary_search_by(|probe| {
8035 let cmp = probe
8036 .end
8037 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
8038 if cmp.is_gt() {
8039 Ordering::Greater
8040 } else {
8041 Ordering::Less
8042 }
8043 }) {
8044 Ok(i) | Err(i) => i,
8045 };
8046 let mut push_region = |start: Option<Point>, end: Option<Point>| {
8047 if let (Some(start_display), Some(end_display)) = (start, end) {
8048 results.push(
8049 start_display.to_display_point(display_snapshot)
8050 ..=end_display.to_display_point(display_snapshot),
8051 );
8052 }
8053 };
8054 let mut start_row: Option<Point> = None;
8055 let mut end_row: Option<Point> = None;
8056 if ranges.len() > count {
8057 return Vec::new();
8058 }
8059 for range in &ranges[start_ix..] {
8060 if range
8061 .start
8062 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
8063 .is_ge()
8064 {
8065 break;
8066 }
8067 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
8068 if let Some(current_row) = &end_row {
8069 if end.row == current_row.row {
8070 continue;
8071 }
8072 }
8073 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
8074 if start_row.is_none() {
8075 assert_eq!(end_row, None);
8076 start_row = Some(start);
8077 end_row = Some(end);
8078 continue;
8079 }
8080 if let Some(current_end) = end_row.as_mut() {
8081 if start.row > current_end.row + 1 {
8082 push_region(start_row, end_row);
8083 start_row = Some(start);
8084 end_row = Some(end);
8085 } else {
8086 // Merge two hunks.
8087 *current_end = end;
8088 }
8089 } else {
8090 unreachable!();
8091 }
8092 }
8093 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
8094 push_region(start_row, end_row);
8095 results
8096 }
8097
8098 pub fn highlight_text<T: 'static>(
8099 &mut self,
8100 ranges: Vec<Range<Anchor>>,
8101 style: HighlightStyle,
8102 cx: &mut ViewContext<Self>,
8103 ) {
8104 self.display_map.update(cx, |map, _| {
8105 map.highlight_text(TypeId::of::<T>(), ranges, style)
8106 });
8107 cx.notify();
8108 }
8109
8110 pub fn highlight_inlays<T: 'static>(
8111 &mut self,
8112 highlights: Vec<InlayHighlight>,
8113 style: HighlightStyle,
8114 cx: &mut ViewContext<Self>,
8115 ) {
8116 self.display_map.update(cx, |map, _| {
8117 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
8118 });
8119 cx.notify();
8120 }
8121
8122 pub fn text_highlights<'a, T: 'static>(
8123 &'a self,
8124 cx: &'a AppContext,
8125 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
8126 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
8127 }
8128
8129 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
8130 let cleared = self
8131 .display_map
8132 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
8133 if cleared {
8134 cx.notify();
8135 }
8136 }
8137
8138 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
8139 self.blink_manager.read(cx).visible() && self.focused
8140 }
8141
8142 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
8143 cx.notify();
8144 }
8145
8146 fn on_buffer_event(
8147 &mut self,
8148 multibuffer: ModelHandle<MultiBuffer>,
8149 event: &multi_buffer::Event,
8150 cx: &mut ViewContext<Self>,
8151 ) {
8152 match event {
8153 multi_buffer::Event::Edited {
8154 sigleton_buffer_edited,
8155 } => {
8156 self.refresh_active_diagnostics(cx);
8157 self.refresh_code_actions(cx);
8158 if self.has_active_copilot_suggestion(cx) {
8159 self.update_visible_copilot_suggestion(cx);
8160 }
8161 cx.emit(Event::BufferEdited);
8162
8163 if *sigleton_buffer_edited {
8164 if let Some(project) = &self.project {
8165 let project = project.read(cx);
8166 let languages_affected = multibuffer
8167 .read(cx)
8168 .all_buffers()
8169 .into_iter()
8170 .filter_map(|buffer| {
8171 let buffer = buffer.read(cx);
8172 let language = buffer.language()?;
8173 if project.is_local()
8174 && project.language_servers_for_buffer(buffer, cx).count() == 0
8175 {
8176 None
8177 } else {
8178 Some(language)
8179 }
8180 })
8181 .cloned()
8182 .collect::<HashSet<_>>();
8183 if !languages_affected.is_empty() {
8184 self.refresh_inlay_hints(
8185 InlayHintRefreshReason::BufferEdited(languages_affected),
8186 cx,
8187 );
8188 }
8189 }
8190 }
8191 }
8192 multi_buffer::Event::ExcerptsAdded {
8193 buffer,
8194 predecessor,
8195 excerpts,
8196 } => {
8197 cx.emit(Event::ExcerptsAdded {
8198 buffer: buffer.clone(),
8199 predecessor: *predecessor,
8200 excerpts: excerpts.clone(),
8201 });
8202 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
8203 }
8204 multi_buffer::Event::ExcerptsRemoved { ids } => {
8205 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
8206 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
8207 }
8208 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
8209 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
8210 multi_buffer::Event::Saved => cx.emit(Event::Saved),
8211 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
8212 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
8213 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
8214 multi_buffer::Event::Closed => cx.emit(Event::Closed),
8215 multi_buffer::Event::DiagnosticsUpdated => {
8216 self.refresh_active_diagnostics(cx);
8217 }
8218 _ => {}
8219 };
8220 }
8221
8222 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
8223 cx.notify();
8224 }
8225
8226 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
8227 self.refresh_copilot_suggestions(true, cx);
8228 self.refresh_inlay_hints(
8229 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
8230 self.selections.newest_anchor().head(),
8231 &self.buffer.read(cx).snapshot(cx),
8232 cx,
8233 )),
8234 cx,
8235 );
8236 }
8237
8238 pub fn set_searchable(&mut self, searchable: bool) {
8239 self.searchable = searchable;
8240 }
8241
8242 pub fn searchable(&self) -> bool {
8243 self.searchable
8244 }
8245
8246 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
8247 let active_item = workspace.active_item(cx);
8248 let editor_handle = if let Some(editor) = active_item
8249 .as_ref()
8250 .and_then(|item| item.act_as::<Self>(cx))
8251 {
8252 editor
8253 } else {
8254 cx.propagate_action();
8255 return;
8256 };
8257
8258 let editor = editor_handle.read(cx);
8259 let buffer = editor.buffer.read(cx);
8260 if buffer.is_singleton() {
8261 cx.propagate_action();
8262 return;
8263 }
8264
8265 let mut new_selections_by_buffer = HashMap::default();
8266 for selection in editor.selections.all::<usize>(cx) {
8267 for (buffer, mut range, _) in
8268 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
8269 {
8270 if selection.reversed {
8271 mem::swap(&mut range.start, &mut range.end);
8272 }
8273 new_selections_by_buffer
8274 .entry(buffer)
8275 .or_insert(Vec::new())
8276 .push(range)
8277 }
8278 }
8279
8280 editor_handle.update(cx, |editor, cx| {
8281 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
8282 });
8283 let pane = workspace.active_pane().clone();
8284 pane.update(cx, |pane, _| pane.disable_history());
8285
8286 // We defer the pane interaction because we ourselves are a workspace item
8287 // and activating a new item causes the pane to call a method on us reentrantly,
8288 // which panics if we're on the stack.
8289 cx.defer(move |workspace, cx| {
8290 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
8291 let editor = workspace.open_project_item::<Self>(buffer, cx);
8292 editor.update(cx, |editor, cx| {
8293 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8294 s.select_ranges(ranges);
8295 });
8296 });
8297 }
8298
8299 pane.update(cx, |pane, _| pane.enable_history());
8300 });
8301 }
8302
8303 fn jump(
8304 workspace: &mut Workspace,
8305 path: ProjectPath,
8306 position: Point,
8307 anchor: language::Anchor,
8308 cx: &mut ViewContext<Workspace>,
8309 ) {
8310 let editor = workspace.open_path(path, None, true, cx);
8311 cx.spawn(|_, mut cx| async move {
8312 let editor = editor
8313 .await?
8314 .downcast::<Editor>()
8315 .ok_or_else(|| anyhow!("opened item was not an editor"))?
8316 .downgrade();
8317 editor.update(&mut cx, |editor, cx| {
8318 let buffer = editor
8319 .buffer()
8320 .read(cx)
8321 .as_singleton()
8322 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
8323 let buffer = buffer.read(cx);
8324 let cursor = if buffer.can_resolve(&anchor) {
8325 language::ToPoint::to_point(&anchor, buffer)
8326 } else {
8327 buffer.clip_point(position, Bias::Left)
8328 };
8329
8330 let nav_history = editor.nav_history.take();
8331 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
8332 s.select_ranges([cursor..cursor]);
8333 });
8334 editor.nav_history = nav_history;
8335
8336 anyhow::Ok(())
8337 })??;
8338
8339 anyhow::Ok(())
8340 })
8341 .detach_and_log_err(cx);
8342 }
8343
8344 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
8345 let snapshot = self.buffer.read(cx).read(cx);
8346 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
8347 Some(
8348 ranges
8349 .iter()
8350 .map(move |range| {
8351 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
8352 })
8353 .collect(),
8354 )
8355 }
8356
8357 fn selection_replacement_ranges(
8358 &self,
8359 range: Range<OffsetUtf16>,
8360 cx: &AppContext,
8361 ) -> Vec<Range<OffsetUtf16>> {
8362 let selections = self.selections.all::<OffsetUtf16>(cx);
8363 let newest_selection = selections
8364 .iter()
8365 .max_by_key(|selection| selection.id)
8366 .unwrap();
8367 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
8368 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
8369 let snapshot = self.buffer.read(cx).read(cx);
8370 selections
8371 .into_iter()
8372 .map(|mut selection| {
8373 selection.start.0 =
8374 (selection.start.0 as isize).saturating_add(start_delta) as usize;
8375 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
8376 snapshot.clip_offset_utf16(selection.start, Bias::Left)
8377 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
8378 })
8379 .collect()
8380 }
8381
8382 fn report_copilot_event(
8383 &self,
8384 suggestion_id: Option<String>,
8385 suggestion_accepted: bool,
8386 cx: &AppContext,
8387 ) {
8388 let Some(project) = &self.project else { return };
8389
8390 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
8391 let file_extension = self
8392 .buffer
8393 .read(cx)
8394 .as_singleton()
8395 .and_then(|b| b.read(cx).file())
8396 .and_then(|file| Path::new(file.file_name(cx)).extension())
8397 .and_then(|e| e.to_str())
8398 .map(|a| a.to_string());
8399
8400 let telemetry = project.read(cx).client().telemetry().clone();
8401 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8402
8403 let event = ClickhouseEvent::Copilot {
8404 suggestion_id,
8405 suggestion_accepted,
8406 file_extension,
8407 };
8408 telemetry.report_clickhouse_event(event, telemetry_settings);
8409 }
8410
8411 fn report_editor_event(
8412 &self,
8413 operation: &'static str,
8414 file_extension: Option<String>,
8415 cx: &AppContext,
8416 ) {
8417 let Some(project) = &self.project else { return };
8418
8419 // If None, we are in a file without an extension
8420 let file = self
8421 .buffer
8422 .read(cx)
8423 .as_singleton()
8424 .and_then(|b| b.read(cx).file());
8425 let file_extension = file_extension.or(file
8426 .as_ref()
8427 .and_then(|file| Path::new(file.file_name(cx)).extension())
8428 .and_then(|e| e.to_str())
8429 .map(|a| a.to_string()));
8430
8431 let vim_mode = cx
8432 .global::<SettingsStore>()
8433 .raw_user_settings()
8434 .get("vim_mode")
8435 == Some(&serde_json::Value::Bool(true));
8436 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
8437 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
8438 let copilot_enabled_for_language = self
8439 .buffer
8440 .read(cx)
8441 .settings_at(0, cx)
8442 .show_copilot_suggestions;
8443
8444 let telemetry = project.read(cx).client().telemetry().clone();
8445 let event = ClickhouseEvent::Editor {
8446 file_extension,
8447 vim_mode,
8448 operation,
8449 copilot_enabled,
8450 copilot_enabled_for_language,
8451 };
8452 telemetry.report_clickhouse_event(event, telemetry_settings)
8453 }
8454
8455 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
8456 /// with each line being an array of {text, highlight} objects.
8457 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
8458 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
8459 return;
8460 };
8461
8462 #[derive(Serialize)]
8463 struct Chunk<'a> {
8464 text: String,
8465 highlight: Option<&'a str>,
8466 }
8467
8468 let snapshot = buffer.read(cx).snapshot();
8469 let range = self
8470 .selected_text_range(cx)
8471 .and_then(|selected_range| {
8472 if selected_range.is_empty() {
8473 None
8474 } else {
8475 Some(selected_range)
8476 }
8477 })
8478 .unwrap_or_else(|| 0..snapshot.len());
8479
8480 let chunks = snapshot.chunks(range, true);
8481 let mut lines = Vec::new();
8482 let mut line: VecDeque<Chunk> = VecDeque::new();
8483
8484 let theme = &theme::current(cx).editor.syntax;
8485
8486 for chunk in chunks {
8487 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
8488 let mut chunk_lines = chunk.text.split("\n").peekable();
8489 while let Some(text) = chunk_lines.next() {
8490 let mut merged_with_last_token = false;
8491 if let Some(last_token) = line.back_mut() {
8492 if last_token.highlight == highlight {
8493 last_token.text.push_str(text);
8494 merged_with_last_token = true;
8495 }
8496 }
8497
8498 if !merged_with_last_token {
8499 line.push_back(Chunk {
8500 text: text.into(),
8501 highlight,
8502 });
8503 }
8504
8505 if chunk_lines.peek().is_some() {
8506 if line.len() > 1 && line.front().unwrap().text.is_empty() {
8507 line.pop_front();
8508 }
8509 if line.len() > 1 && line.back().unwrap().text.is_empty() {
8510 line.pop_back();
8511 }
8512
8513 lines.push(mem::take(&mut line));
8514 }
8515 }
8516 }
8517
8518 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
8519 return;
8520 };
8521 cx.write_to_clipboard(ClipboardItem::new(lines));
8522 }
8523
8524 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
8525 &self.inlay_hint_cache
8526 }
8527
8528 pub fn replay_insert_event(
8529 &mut self,
8530 text: &str,
8531 relative_utf16_range: Option<Range<isize>>,
8532 cx: &mut ViewContext<Self>,
8533 ) {
8534 if !self.input_enabled {
8535 cx.emit(Event::InputIgnored { text: text.into() });
8536 return;
8537 }
8538 if let Some(relative_utf16_range) = relative_utf16_range {
8539 let selections = self.selections.all::<OffsetUtf16>(cx);
8540 self.change_selections(None, cx, |s| {
8541 let new_ranges = selections.into_iter().map(|range| {
8542 let start = OffsetUtf16(
8543 range
8544 .head()
8545 .0
8546 .saturating_add_signed(relative_utf16_range.start),
8547 );
8548 let end = OffsetUtf16(
8549 range
8550 .head()
8551 .0
8552 .saturating_add_signed(relative_utf16_range.end),
8553 );
8554 start..end
8555 });
8556 s.select_ranges(new_ranges);
8557 });
8558 }
8559
8560 self.handle_input(text, cx);
8561 }
8562}
8563
8564fn inlay_hint_settings(
8565 location: Anchor,
8566 snapshot: &MultiBufferSnapshot,
8567 cx: &mut ViewContext<'_, '_, Editor>,
8568) -> InlayHintSettings {
8569 let file = snapshot.file_at(location);
8570 let language = snapshot.language_at(location);
8571 let settings = all_language_settings(file, cx);
8572 settings
8573 .language(language.map(|l| l.name()).as_deref())
8574 .inlay_hints
8575}
8576
8577fn consume_contiguous_rows(
8578 contiguous_row_selections: &mut Vec<Selection<Point>>,
8579 selection: &Selection<Point>,
8580 display_map: &DisplaySnapshot,
8581 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
8582) -> (u32, u32) {
8583 contiguous_row_selections.push(selection.clone());
8584 let start_row = selection.start.row;
8585 let mut end_row = ending_row(selection, display_map);
8586
8587 while let Some(next_selection) = selections.peek() {
8588 if next_selection.start.row <= end_row {
8589 end_row = ending_row(next_selection, display_map);
8590 contiguous_row_selections.push(selections.next().unwrap().clone());
8591 } else {
8592 break;
8593 }
8594 }
8595 (start_row, end_row)
8596}
8597
8598fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8599 if next_selection.end.column > 0 || next_selection.is_empty() {
8600 display_map.next_line_boundary(next_selection.end).0.row + 1
8601 } else {
8602 next_selection.end.row
8603 }
8604}
8605
8606impl EditorSnapshot {
8607 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8608 self.display_snapshot.buffer_snapshot.language_at(position)
8609 }
8610
8611 pub fn is_focused(&self) -> bool {
8612 self.is_focused
8613 }
8614
8615 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8616 self.placeholder_text.as_ref()
8617 }
8618
8619 pub fn scroll_position(&self) -> Vector2F {
8620 self.scroll_anchor.scroll_position(&self.display_snapshot)
8621 }
8622}
8623
8624impl Deref for EditorSnapshot {
8625 type Target = DisplaySnapshot;
8626
8627 fn deref(&self) -> &Self::Target {
8628 &self.display_snapshot
8629 }
8630}
8631
8632#[derive(Clone, Debug, PartialEq, Eq)]
8633pub enum Event {
8634 InputIgnored {
8635 text: Arc<str>,
8636 },
8637 InputHandled {
8638 utf16_range_to_replace: Option<Range<isize>>,
8639 text: Arc<str>,
8640 },
8641 ExcerptsAdded {
8642 buffer: ModelHandle<Buffer>,
8643 predecessor: ExcerptId,
8644 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8645 },
8646 ExcerptsRemoved {
8647 ids: Vec<ExcerptId>,
8648 },
8649 BufferEdited,
8650 Edited,
8651 Reparsed,
8652 Focused,
8653 Blurred,
8654 DirtyChanged,
8655 Saved,
8656 TitleChanged,
8657 DiffBaseChanged,
8658 SelectionsChanged {
8659 local: bool,
8660 },
8661 ScrollPositionChanged {
8662 local: bool,
8663 autoscroll: bool,
8664 },
8665 Closed,
8666}
8667
8668pub struct EditorFocused(pub ViewHandle<Editor>);
8669pub struct EditorBlurred(pub ViewHandle<Editor>);
8670pub struct EditorReleased(pub WeakViewHandle<Editor>);
8671
8672impl Entity for Editor {
8673 type Event = Event;
8674
8675 fn release(&mut self, cx: &mut AppContext) {
8676 cx.emit_global(EditorReleased(self.handle.clone()));
8677 }
8678}
8679
8680impl View for Editor {
8681 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8682 let style = self.style(cx);
8683 let font_changed = self.display_map.update(cx, |map, cx| {
8684 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8685 map.set_font(style.text.font_id, style.text.font_size, cx)
8686 });
8687
8688 if font_changed {
8689 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8690 hide_hover(editor, cx);
8691 hide_link_definition(editor, cx);
8692 });
8693 }
8694
8695 Stack::new()
8696 .with_child(EditorElement::new(style.clone()))
8697 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8698 .into_any()
8699 }
8700
8701 fn ui_name() -> &'static str {
8702 "Editor"
8703 }
8704
8705 fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
8706 if cx.is_self_focused() {
8707 let focused_event = EditorFocused(cx.handle());
8708 cx.emit(Event::Focused);
8709 cx.emit_global(focused_event);
8710 }
8711 if let Some(rename) = self.pending_rename.as_ref() {
8712 cx.focus(&rename.editor);
8713 } else if cx.is_self_focused() || !focused.is::<Editor>() {
8714 if !self.focused {
8715 self.blink_manager.update(cx, BlinkManager::enable);
8716 }
8717 self.focused = true;
8718 self.buffer.update(cx, |buffer, cx| {
8719 buffer.finalize_last_transaction(cx);
8720 if self.leader_replica_id.is_none() {
8721 buffer.set_active_selections(
8722 &self.selections.disjoint_anchors(),
8723 self.selections.line_mode,
8724 self.cursor_shape,
8725 cx,
8726 );
8727 }
8728 });
8729 }
8730 }
8731
8732 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8733 let blurred_event = EditorBlurred(cx.handle());
8734 cx.emit_global(blurred_event);
8735 self.focused = false;
8736 self.blink_manager.update(cx, BlinkManager::disable);
8737 self.buffer
8738 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8739 self.hide_context_menu(cx);
8740 hide_hover(self, cx);
8741 cx.emit(Event::Blurred);
8742 cx.notify();
8743 }
8744
8745 fn modifiers_changed(
8746 &mut self,
8747 event: &gpui::platform::ModifiersChangedEvent,
8748 cx: &mut ViewContext<Self>,
8749 ) -> bool {
8750 let pending_selection = self.has_pending_selection();
8751
8752 if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
8753 if event.cmd && !pending_selection {
8754 let point = point.clone();
8755 let snapshot = self.snapshot(cx);
8756 let kind = point.definition_kind(event.shift);
8757
8758 show_link_definition(kind, self, point, snapshot, cx);
8759 return false;
8760 }
8761 }
8762
8763 {
8764 if self.link_go_to_definition_state.symbol_range.is_some()
8765 || !self.link_go_to_definition_state.definitions.is_empty()
8766 {
8767 self.link_go_to_definition_state.symbol_range.take();
8768 self.link_go_to_definition_state.definitions.clear();
8769 cx.notify();
8770 }
8771
8772 self.link_go_to_definition_state.task = None;
8773
8774 self.clear_highlights::<LinkGoToDefinitionState>(cx);
8775 }
8776
8777 false
8778 }
8779
8780 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8781 Self::reset_to_default_keymap_context(keymap);
8782 let mode = match self.mode {
8783 EditorMode::SingleLine => "single_line",
8784 EditorMode::AutoHeight { .. } => "auto_height",
8785 EditorMode::Full => "full",
8786 };
8787 keymap.add_key("mode", mode);
8788 if self.pending_rename.is_some() {
8789 keymap.add_identifier("renaming");
8790 }
8791 if self.context_menu_visible() {
8792 match self.context_menu.as_ref() {
8793 Some(ContextMenu::Completions(_)) => {
8794 keymap.add_identifier("menu");
8795 keymap.add_identifier("showing_completions")
8796 }
8797 Some(ContextMenu::CodeActions(_)) => {
8798 keymap.add_identifier("menu");
8799 keymap.add_identifier("showing_code_actions")
8800 }
8801 None => {}
8802 }
8803 }
8804
8805 for layer in self.keymap_context_layers.values() {
8806 keymap.extend(layer);
8807 }
8808
8809 if let Some(extension) = self
8810 .buffer
8811 .read(cx)
8812 .as_singleton()
8813 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8814 {
8815 keymap.add_key("extension", extension.to_string());
8816 }
8817 }
8818
8819 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8820 Some(
8821 self.buffer
8822 .read(cx)
8823 .read(cx)
8824 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8825 .collect(),
8826 )
8827 }
8828
8829 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8830 // Prevent the IME menu from appearing when holding down an alphabetic key
8831 // while input is disabled.
8832 if !self.input_enabled {
8833 return None;
8834 }
8835
8836 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8837 Some(range.start.0..range.end.0)
8838 }
8839
8840 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8841 let snapshot = self.buffer.read(cx).read(cx);
8842 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8843 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8844 }
8845
8846 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8847 self.clear_highlights::<InputComposition>(cx);
8848 self.ime_transaction.take();
8849 }
8850
8851 fn replace_text_in_range(
8852 &mut self,
8853 range_utf16: Option<Range<usize>>,
8854 text: &str,
8855 cx: &mut ViewContext<Self>,
8856 ) {
8857 if !self.input_enabled {
8858 cx.emit(Event::InputIgnored { text: text.into() });
8859 return;
8860 }
8861
8862 self.transact(cx, |this, cx| {
8863 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8864 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8865 Some(this.selection_replacement_ranges(range_utf16, cx))
8866 } else {
8867 this.marked_text_ranges(cx)
8868 };
8869
8870 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
8871 let newest_selection_id = this.selections.newest_anchor().id;
8872 this.selections
8873 .all::<OffsetUtf16>(cx)
8874 .iter()
8875 .zip(ranges_to_replace.iter())
8876 .find_map(|(selection, range)| {
8877 if selection.id == newest_selection_id {
8878 Some(
8879 (range.start.0 as isize - selection.head().0 as isize)
8880 ..(range.end.0 as isize - selection.head().0 as isize),
8881 )
8882 } else {
8883 None
8884 }
8885 })
8886 });
8887
8888 cx.emit(Event::InputHandled {
8889 utf16_range_to_replace: range_to_replace,
8890 text: text.into(),
8891 });
8892
8893 if let Some(new_selected_ranges) = new_selected_ranges {
8894 this.change_selections(None, cx, |selections| {
8895 selections.select_ranges(new_selected_ranges)
8896 });
8897 }
8898
8899 this.handle_input(text, cx);
8900 });
8901
8902 if let Some(transaction) = self.ime_transaction {
8903 self.buffer.update(cx, |buffer, cx| {
8904 buffer.group_until_transaction(transaction, cx);
8905 });
8906 }
8907
8908 self.unmark_text(cx);
8909 }
8910
8911 fn replace_and_mark_text_in_range(
8912 &mut self,
8913 range_utf16: Option<Range<usize>>,
8914 text: &str,
8915 new_selected_range_utf16: Option<Range<usize>>,
8916 cx: &mut ViewContext<Self>,
8917 ) {
8918 if !self.input_enabled {
8919 cx.emit(Event::InputIgnored { text: text.into() });
8920 return;
8921 }
8922
8923 let transaction = self.transact(cx, |this, cx| {
8924 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8925 let snapshot = this.buffer.read(cx).read(cx);
8926 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8927 for marked_range in &mut marked_ranges {
8928 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8929 marked_range.start.0 += relative_range_utf16.start;
8930 marked_range.start =
8931 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8932 marked_range.end =
8933 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8934 }
8935 }
8936 Some(marked_ranges)
8937 } else if let Some(range_utf16) = range_utf16 {
8938 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8939 Some(this.selection_replacement_ranges(range_utf16, cx))
8940 } else {
8941 None
8942 };
8943
8944 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
8945 let newest_selection_id = this.selections.newest_anchor().id;
8946 this.selections
8947 .all::<OffsetUtf16>(cx)
8948 .iter()
8949 .zip(ranges_to_replace.iter())
8950 .find_map(|(selection, range)| {
8951 if selection.id == newest_selection_id {
8952 Some(
8953 (range.start.0 as isize - selection.head().0 as isize)
8954 ..(range.end.0 as isize - selection.head().0 as isize),
8955 )
8956 } else {
8957 None
8958 }
8959 })
8960 });
8961
8962 cx.emit(Event::InputHandled {
8963 utf16_range_to_replace: range_to_replace,
8964 text: text.into(),
8965 });
8966
8967 if let Some(ranges) = ranges_to_replace {
8968 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8969 }
8970
8971 let marked_ranges = {
8972 let snapshot = this.buffer.read(cx).read(cx);
8973 this.selections
8974 .disjoint_anchors()
8975 .iter()
8976 .map(|selection| {
8977 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8978 })
8979 .collect::<Vec<_>>()
8980 };
8981
8982 if text.is_empty() {
8983 this.unmark_text(cx);
8984 } else {
8985 this.highlight_text::<InputComposition>(
8986 marked_ranges.clone(),
8987 this.style(cx).composition_mark,
8988 cx,
8989 );
8990 }
8991
8992 this.handle_input(text, cx);
8993
8994 if let Some(new_selected_range) = new_selected_range_utf16 {
8995 let snapshot = this.buffer.read(cx).read(cx);
8996 let new_selected_ranges = marked_ranges
8997 .into_iter()
8998 .map(|marked_range| {
8999 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
9000 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
9001 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
9002 snapshot.clip_offset_utf16(new_start, Bias::Left)
9003 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
9004 })
9005 .collect::<Vec<_>>();
9006
9007 drop(snapshot);
9008 this.change_selections(None, cx, |selections| {
9009 selections.select_ranges(new_selected_ranges)
9010 });
9011 }
9012 });
9013
9014 self.ime_transaction = self.ime_transaction.or(transaction);
9015 if let Some(transaction) = self.ime_transaction {
9016 self.buffer.update(cx, |buffer, cx| {
9017 buffer.group_until_transaction(transaction, cx);
9018 });
9019 }
9020
9021 if self.text_highlights::<InputComposition>(cx).is_none() {
9022 self.ime_transaction.take();
9023 }
9024 }
9025}
9026
9027fn build_style(
9028 settings: &ThemeSettings,
9029 get_field_editor_theme: Option<&GetFieldEditorTheme>,
9030 override_text_style: Option<&OverrideTextStyle>,
9031 cx: &AppContext,
9032) -> EditorStyle {
9033 let font_cache = cx.font_cache();
9034 let line_height_scalar = settings.line_height();
9035 let theme_id = settings.theme.meta.id;
9036 let mut theme = settings.theme.editor.clone();
9037 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
9038 let field_editor_theme = get_field_editor_theme(&settings.theme);
9039 theme.text_color = field_editor_theme.text.color;
9040 theme.selection = field_editor_theme.selection;
9041 theme.background = field_editor_theme
9042 .container
9043 .background_color
9044 .unwrap_or_default();
9045 EditorStyle {
9046 text: field_editor_theme.text,
9047 placeholder_text: field_editor_theme.placeholder_text,
9048 line_height_scalar,
9049 theme,
9050 theme_id,
9051 }
9052 } else {
9053 let font_family_id = settings.buffer_font_family;
9054 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
9055 let font_properties = Default::default();
9056 let font_id = font_cache
9057 .select_font(font_family_id, &font_properties)
9058 .unwrap();
9059 let font_size = settings.buffer_font_size(cx);
9060 EditorStyle {
9061 text: TextStyle {
9062 color: settings.theme.editor.text_color,
9063 font_family_name,
9064 font_family_id,
9065 font_id,
9066 font_size,
9067 font_properties,
9068 underline: Default::default(),
9069 soft_wrap: false,
9070 },
9071 placeholder_text: None,
9072 line_height_scalar,
9073 theme,
9074 theme_id,
9075 }
9076 };
9077
9078 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
9079 if let Some(highlighted) = style
9080 .text
9081 .clone()
9082 .highlight(highlight_style, font_cache)
9083 .log_err()
9084 {
9085 style.text = highlighted;
9086 }
9087 }
9088
9089 style
9090}
9091
9092trait SelectionExt {
9093 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
9094 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
9095 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
9096 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
9097 -> Range<u32>;
9098}
9099
9100impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
9101 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
9102 let start = self.start.to_point(buffer);
9103 let end = self.end.to_point(buffer);
9104 if self.reversed {
9105 end..start
9106 } else {
9107 start..end
9108 }
9109 }
9110
9111 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
9112 let start = self.start.to_offset(buffer);
9113 let end = self.end.to_offset(buffer);
9114 if self.reversed {
9115 end..start
9116 } else {
9117 start..end
9118 }
9119 }
9120
9121 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
9122 let start = self
9123 .start
9124 .to_point(&map.buffer_snapshot)
9125 .to_display_point(map);
9126 let end = self
9127 .end
9128 .to_point(&map.buffer_snapshot)
9129 .to_display_point(map);
9130 if self.reversed {
9131 end..start
9132 } else {
9133 start..end
9134 }
9135 }
9136
9137 fn spanned_rows(
9138 &self,
9139 include_end_if_at_line_start: bool,
9140 map: &DisplaySnapshot,
9141 ) -> Range<u32> {
9142 let start = self.start.to_point(&map.buffer_snapshot);
9143 let mut end = self.end.to_point(&map.buffer_snapshot);
9144 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
9145 end.row -= 1;
9146 }
9147
9148 let buffer_start = map.prev_line_boundary(start).0;
9149 let buffer_end = map.next_line_boundary(end).0;
9150 buffer_start.row..buffer_end.row + 1
9151 }
9152}
9153
9154impl<T: InvalidationRegion> InvalidationStack<T> {
9155 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
9156 where
9157 S: Clone + ToOffset,
9158 {
9159 while let Some(region) = self.last() {
9160 let all_selections_inside_invalidation_ranges =
9161 if selections.len() == region.ranges().len() {
9162 selections
9163 .iter()
9164 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
9165 .all(|(selection, invalidation_range)| {
9166 let head = selection.head().to_offset(buffer);
9167 invalidation_range.start <= head && invalidation_range.end >= head
9168 })
9169 } else {
9170 false
9171 };
9172
9173 if all_selections_inside_invalidation_ranges {
9174 break;
9175 } else {
9176 self.pop();
9177 }
9178 }
9179 }
9180}
9181
9182impl<T> Default for InvalidationStack<T> {
9183 fn default() -> Self {
9184 Self(Default::default())
9185 }
9186}
9187
9188impl<T> Deref for InvalidationStack<T> {
9189 type Target = Vec<T>;
9190
9191 fn deref(&self) -> &Self::Target {
9192 &self.0
9193 }
9194}
9195
9196impl<T> DerefMut for InvalidationStack<T> {
9197 fn deref_mut(&mut self) -> &mut Self::Target {
9198 &mut self.0
9199 }
9200}
9201
9202impl InvalidationRegion for SnippetState {
9203 fn ranges(&self) -> &[Range<Anchor>] {
9204 &self.ranges[self.active_index]
9205 }
9206}
9207
9208impl Deref for EditorStyle {
9209 type Target = theme::Editor;
9210
9211 fn deref(&self) -> &Self::Target {
9212 &self.theme
9213 }
9214}
9215
9216pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
9217 let mut highlighted_lines = Vec::new();
9218
9219 for (index, line) in diagnostic.message.lines().enumerate() {
9220 let line = match &diagnostic.source {
9221 Some(source) if index == 0 => {
9222 let source_highlight = Vec::from_iter(0..source.len());
9223 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
9224 }
9225
9226 _ => highlight_diagnostic_message(Vec::new(), line),
9227 };
9228 highlighted_lines.push(line);
9229 }
9230 let message = diagnostic.message;
9231 Arc::new(move |cx: &mut BlockContext| {
9232 let message = message.clone();
9233 let settings = settings::get::<ThemeSettings>(cx);
9234 let tooltip_style = settings.theme.tooltip.clone();
9235 let theme = &settings.theme.editor;
9236 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
9237 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
9238 let anchor_x = cx.anchor_x;
9239 enum BlockContextToolip {}
9240 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
9241 Flex::column()
9242 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
9243 Label::new(
9244 line.clone(),
9245 style.message.clone().with_font_size(font_size),
9246 )
9247 .with_highlights(highlights.clone())
9248 .contained()
9249 .with_margin_left(anchor_x)
9250 }))
9251 .aligned()
9252 .left()
9253 .into_any()
9254 })
9255 .with_cursor_style(CursorStyle::PointingHand)
9256 .on_click(MouseButton::Left, move |_, _, cx| {
9257 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
9258 })
9259 // We really need to rethink this ID system...
9260 .with_tooltip::<BlockContextToolip>(
9261 cx.block_id,
9262 "Copy diagnostic message",
9263 None,
9264 tooltip_style,
9265 cx,
9266 )
9267 .into_any()
9268 })
9269}
9270
9271pub fn highlight_diagnostic_message(
9272 initial_highlights: Vec<usize>,
9273 message: &str,
9274) -> (String, Vec<usize>) {
9275 let mut message_without_backticks = String::new();
9276 let mut prev_offset = 0;
9277 let mut inside_block = false;
9278 let mut highlights = initial_highlights;
9279 for (match_ix, (offset, _)) in message
9280 .match_indices('`')
9281 .chain([(message.len(), "")])
9282 .enumerate()
9283 {
9284 message_without_backticks.push_str(&message[prev_offset..offset]);
9285 if inside_block {
9286 highlights.extend(prev_offset - match_ix..offset - match_ix);
9287 }
9288
9289 inside_block = !inside_block;
9290 prev_offset = offset + 1;
9291 }
9292
9293 (message_without_backticks, highlights)
9294}
9295
9296pub fn diagnostic_style(
9297 severity: DiagnosticSeverity,
9298 valid: bool,
9299 theme: &theme::Editor,
9300) -> DiagnosticStyle {
9301 match (severity, valid) {
9302 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
9303 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
9304 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
9305 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
9306 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
9307 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
9308 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
9309 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
9310 _ => theme.invalid_hint_diagnostic.clone(),
9311 }
9312}
9313
9314pub fn combine_syntax_and_fuzzy_match_highlights(
9315 text: &str,
9316 default_style: HighlightStyle,
9317 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
9318 match_indices: &[usize],
9319) -> Vec<(Range<usize>, HighlightStyle)> {
9320 let mut result = Vec::new();
9321 let mut match_indices = match_indices.iter().copied().peekable();
9322
9323 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
9324 {
9325 syntax_highlight.weight = None;
9326
9327 // Add highlights for any fuzzy match characters before the next
9328 // syntax highlight range.
9329 while let Some(&match_index) = match_indices.peek() {
9330 if match_index >= range.start {
9331 break;
9332 }
9333 match_indices.next();
9334 let end_index = char_ix_after(match_index, text);
9335 let mut match_style = default_style;
9336 match_style.weight = Some(fonts::Weight::BOLD);
9337 result.push((match_index..end_index, match_style));
9338 }
9339
9340 if range.start == usize::MAX {
9341 break;
9342 }
9343
9344 // Add highlights for any fuzzy match characters within the
9345 // syntax highlight range.
9346 let mut offset = range.start;
9347 while let Some(&match_index) = match_indices.peek() {
9348 if match_index >= range.end {
9349 break;
9350 }
9351
9352 match_indices.next();
9353 if match_index > offset {
9354 result.push((offset..match_index, syntax_highlight));
9355 }
9356
9357 let mut end_index = char_ix_after(match_index, text);
9358 while let Some(&next_match_index) = match_indices.peek() {
9359 if next_match_index == end_index && next_match_index < range.end {
9360 end_index = char_ix_after(next_match_index, text);
9361 match_indices.next();
9362 } else {
9363 break;
9364 }
9365 }
9366
9367 let mut match_style = syntax_highlight;
9368 match_style.weight = Some(fonts::Weight::BOLD);
9369 result.push((match_index..end_index, match_style));
9370 offset = end_index;
9371 }
9372
9373 if offset < range.end {
9374 result.push((offset..range.end, syntax_highlight));
9375 }
9376 }
9377
9378 fn char_ix_after(ix: usize, text: &str) -> usize {
9379 ix + text[ix..].chars().next().unwrap().len_utf8()
9380 }
9381
9382 result
9383}
9384
9385pub fn styled_runs_for_code_label<'a>(
9386 label: &'a CodeLabel,
9387 syntax_theme: &'a theme::SyntaxTheme,
9388) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
9389 let fade_out = HighlightStyle {
9390 fade_out: Some(0.35),
9391 ..Default::default()
9392 };
9393
9394 let mut prev_end = label.filter_range.end;
9395 label
9396 .runs
9397 .iter()
9398 .enumerate()
9399 .flat_map(move |(ix, (range, highlight_id))| {
9400 let style = if let Some(style) = highlight_id.style(syntax_theme) {
9401 style
9402 } else {
9403 return Default::default();
9404 };
9405 let mut muted_style = style;
9406 muted_style.highlight(fade_out);
9407
9408 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
9409 if range.start >= label.filter_range.end {
9410 if range.start > prev_end {
9411 runs.push((prev_end..range.start, fade_out));
9412 }
9413 runs.push((range.clone(), muted_style));
9414 } else if range.end <= label.filter_range.end {
9415 runs.push((range.clone(), style));
9416 } else {
9417 runs.push((range.start..label.filter_range.end, style));
9418 runs.push((label.filter_range.end..range.end, muted_style));
9419 }
9420 prev_end = cmp::max(prev_end, range.end);
9421
9422 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
9423 runs.push((prev_end..label.text.len(), fade_out));
9424 }
9425
9426 runs
9427 })
9428}
9429
9430pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
9431 let mut index = 0;
9432 let mut codepoints = text.char_indices().peekable();
9433
9434 std::iter::from_fn(move || {
9435 let start_index = index;
9436 while let Some((new_index, codepoint)) = codepoints.next() {
9437 index = new_index + codepoint.len_utf8();
9438 let current_upper = codepoint.is_uppercase();
9439 let next_upper = codepoints
9440 .peek()
9441 .map(|(_, c)| c.is_uppercase())
9442 .unwrap_or(false);
9443
9444 if !current_upper && next_upper {
9445 return Some(&text[start_index..index]);
9446 }
9447 }
9448
9449 index = text.len();
9450 if start_index < text.len() {
9451 return Some(&text[start_index..]);
9452 }
9453 None
9454 })
9455 .flat_map(|word| word.split_inclusive('_'))
9456 .flat_map(|word| word.split_inclusive('-'))
9457}
9458
9459trait RangeToAnchorExt {
9460 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
9461}
9462
9463impl<T: ToOffset> RangeToAnchorExt for Range<T> {
9464 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
9465 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
9466 }
9467}