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