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