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},
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::<CompletionTag, _>::new(
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::<ActionTag, _>::new(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
2658 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2659 let offset = position.to_offset(buffer);
2660 let (word_range, kind) = buffer.surrounding_word(offset);
2661 if offset > word_range.start && kind == Some(CharKind::Word) {
2662 Some(
2663 buffer
2664 .text_for_range(word_range.start..offset)
2665 .collect::<String>(),
2666 )
2667 } else {
2668 None
2669 }
2670 }
2671
2672 fn refresh_inlays(&mut self, reason: InlayRefreshReason, cx: &mut ViewContext<Self>) {
2673 if self.project.is_none() || self.mode != EditorMode::Full {
2674 return;
2675 }
2676
2677 let (invalidate_cache, required_languages) = match reason {
2678 InlayRefreshReason::SettingsChange(new_settings) => {
2679 match self.inlay_hint_cache.update_settings(
2680 &self.buffer,
2681 new_settings,
2682 self.visible_inlay_hints(cx),
2683 cx,
2684 ) {
2685 ControlFlow::Break(Some(InlaySplice {
2686 to_remove,
2687 to_insert,
2688 })) => {
2689 self.splice_inlay_hints(to_remove, to_insert, cx);
2690 return;
2691 }
2692 ControlFlow::Break(None) => return,
2693 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2694 }
2695 }
2696 InlayRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2697 InlayRefreshReason::BufferEdited(buffer_languages) => {
2698 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2699 }
2700 InlayRefreshReason::RefreshRequested => (InvalidationStrategy::RefreshRequested, None),
2701 };
2702
2703 if let Some(InlaySplice {
2704 to_remove,
2705 to_insert,
2706 }) = self.inlay_hint_cache.spawn_hint_refresh(
2707 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
2708 invalidate_cache,
2709 cx,
2710 ) {
2711 self.splice_inlay_hints(to_remove, to_insert, cx);
2712 }
2713 }
2714
2715 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
2716 self.display_map
2717 .read(cx)
2718 .current_inlays()
2719 .filter(move |inlay| {
2720 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
2721 })
2722 .cloned()
2723 .collect()
2724 }
2725
2726 fn excerpt_visible_offsets(
2727 &self,
2728 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
2729 cx: &mut ViewContext<'_, '_, Editor>,
2730 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
2731 let multi_buffer = self.buffer().read(cx);
2732 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2733 let multi_buffer_visible_start = self
2734 .scroll_manager
2735 .anchor()
2736 .anchor
2737 .to_point(&multi_buffer_snapshot);
2738 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
2739 multi_buffer_visible_start
2740 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
2741 Bias::Left,
2742 );
2743 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
2744 multi_buffer
2745 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
2746 .into_iter()
2747 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
2748 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
2749 let buffer = buffer_handle.read(cx);
2750 let language = buffer.language()?;
2751 if let Some(restrict_to_languages) = restrict_to_languages {
2752 if !restrict_to_languages.contains(language) {
2753 return None;
2754 }
2755 }
2756 Some((
2757 excerpt_id,
2758 (
2759 buffer_handle,
2760 buffer.version().clone(),
2761 excerpt_visible_range,
2762 ),
2763 ))
2764 })
2765 .collect()
2766 }
2767
2768 fn splice_inlay_hints(
2769 &self,
2770 to_remove: Vec<InlayId>,
2771 to_insert: Vec<Inlay>,
2772 cx: &mut ViewContext<Self>,
2773 ) {
2774 self.display_map.update(cx, |display_map, cx| {
2775 display_map.splice_inlays(to_remove, to_insert, cx);
2776 });
2777 }
2778
2779 fn trigger_on_type_formatting(
2780 &self,
2781 input: String,
2782 cx: &mut ViewContext<Self>,
2783 ) -> Option<Task<Result<()>>> {
2784 if input.len() != 1 {
2785 return None;
2786 }
2787
2788 let project = self.project.as_ref()?;
2789 let position = self.selections.newest_anchor().head();
2790 let (buffer, buffer_position) = self
2791 .buffer
2792 .read(cx)
2793 .text_anchor_for_position(position.clone(), cx)?;
2794
2795 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2796 // hence we do LSP request & edit on host side only — add formats to host's history.
2797 let push_to_lsp_host_history = true;
2798 // If this is not the host, append its history with new edits.
2799 let push_to_client_history = project.read(cx).is_remote();
2800
2801 let on_type_formatting = project.update(cx, |project, cx| {
2802 project.on_type_format(
2803 buffer.clone(),
2804 buffer_position,
2805 input,
2806 push_to_lsp_host_history,
2807 cx,
2808 )
2809 });
2810 Some(cx.spawn(|editor, mut cx| async move {
2811 if let Some(transaction) = on_type_formatting.await? {
2812 if push_to_client_history {
2813 buffer.update(&mut cx, |buffer, _| {
2814 buffer.push_transaction(transaction, Instant::now());
2815 });
2816 }
2817 editor.update(&mut cx, |editor, cx| {
2818 editor.refresh_document_highlights(cx);
2819 })?;
2820 }
2821 Ok(())
2822 }))
2823 }
2824
2825 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2826 if self.pending_rename.is_some() {
2827 return;
2828 }
2829
2830 let project = if let Some(project) = self.project.clone() {
2831 project
2832 } else {
2833 return;
2834 };
2835
2836 let position = self.selections.newest_anchor().head();
2837 let (buffer, buffer_position) = if let Some(output) = self
2838 .buffer
2839 .read(cx)
2840 .text_anchor_for_position(position.clone(), cx)
2841 {
2842 output
2843 } else {
2844 return;
2845 };
2846
2847 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2848 let completions = project.update(cx, |project, cx| {
2849 project.completions(&buffer, buffer_position, cx)
2850 });
2851
2852 let id = post_inc(&mut self.next_completion_id);
2853 let task = cx.spawn(|this, mut cx| {
2854 async move {
2855 let menu = if let Some(completions) = completions.await.log_err() {
2856 let mut menu = CompletionsMenu {
2857 id,
2858 initial_position: position,
2859 match_candidates: completions
2860 .iter()
2861 .enumerate()
2862 .map(|(id, completion)| {
2863 StringMatchCandidate::new(
2864 id,
2865 completion.label.text[completion.label.filter_range.clone()]
2866 .into(),
2867 )
2868 })
2869 .collect(),
2870 buffer,
2871 completions: completions.into(),
2872 matches: Vec::new().into(),
2873 selected_item: 0,
2874 list: Default::default(),
2875 };
2876 menu.filter(query.as_deref(), cx.background()).await;
2877 if menu.matches.is_empty() {
2878 None
2879 } else {
2880 Some(menu)
2881 }
2882 } else {
2883 None
2884 };
2885
2886 this.update(&mut cx, |this, cx| {
2887 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2888
2889 match this.context_menu.as_ref() {
2890 None => {}
2891 Some(ContextMenu::Completions(prev_menu)) => {
2892 if prev_menu.id > id {
2893 return;
2894 }
2895 }
2896 _ => return,
2897 }
2898
2899 if this.focused && menu.is_some() {
2900 let menu = menu.unwrap();
2901 this.show_context_menu(ContextMenu::Completions(menu), cx);
2902 } else if this.completion_tasks.is_empty() {
2903 // If there are no more completion tasks and the last menu was
2904 // empty, we should hide it. If it was already hidden, we should
2905 // also show the copilot suggestion when available.
2906 if this.hide_context_menu(cx).is_none() {
2907 this.update_visible_copilot_suggestion(cx);
2908 }
2909 }
2910 })?;
2911
2912 Ok::<_, anyhow::Error>(())
2913 }
2914 .log_err()
2915 });
2916 self.completion_tasks.push((id, task));
2917 }
2918
2919 pub fn confirm_completion(
2920 &mut self,
2921 action: &ConfirmCompletion,
2922 cx: &mut ViewContext<Self>,
2923 ) -> Option<Task<Result<()>>> {
2924 use language::ToOffset as _;
2925
2926 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2927 menu
2928 } else {
2929 return None;
2930 };
2931
2932 let mat = completions_menu
2933 .matches
2934 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2935 let buffer_handle = completions_menu.buffer;
2936 let completion = completions_menu.completions.get(mat.candidate_id)?;
2937
2938 let snippet;
2939 let text;
2940 if completion.is_snippet() {
2941 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2942 text = snippet.as_ref().unwrap().text.clone();
2943 } else {
2944 snippet = None;
2945 text = completion.new_text.clone();
2946 };
2947 let selections = self.selections.all::<usize>(cx);
2948 let buffer = buffer_handle.read(cx);
2949 let old_range = completion.old_range.to_offset(buffer);
2950 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2951
2952 let newest_selection = self.selections.newest_anchor();
2953 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
2954 return None;
2955 }
2956
2957 let lookbehind = newest_selection
2958 .start
2959 .text_anchor
2960 .to_offset(buffer)
2961 .saturating_sub(old_range.start);
2962 let lookahead = old_range
2963 .end
2964 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2965 let mut common_prefix_len = old_text
2966 .bytes()
2967 .zip(text.bytes())
2968 .take_while(|(a, b)| a == b)
2969 .count();
2970
2971 let snapshot = self.buffer.read(cx).snapshot(cx);
2972 let mut ranges = Vec::new();
2973 for selection in &selections {
2974 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2975 let start = selection.start.saturating_sub(lookbehind);
2976 let end = selection.end + lookahead;
2977 ranges.push(start + common_prefix_len..end);
2978 } else {
2979 common_prefix_len = 0;
2980 ranges.clear();
2981 ranges.extend(selections.iter().map(|s| {
2982 if s.id == newest_selection.id {
2983 old_range.clone()
2984 } else {
2985 s.start..s.end
2986 }
2987 }));
2988 break;
2989 }
2990 }
2991 let text = &text[common_prefix_len..];
2992
2993 self.transact(cx, |this, cx| {
2994 if let Some(mut snippet) = snippet {
2995 snippet.text = text.to_string();
2996 for tabstop in snippet.tabstops.iter_mut().flatten() {
2997 tabstop.start -= common_prefix_len as isize;
2998 tabstop.end -= common_prefix_len as isize;
2999 }
3000
3001 this.insert_snippet(&ranges, snippet, cx).log_err();
3002 } else {
3003 this.buffer.update(cx, |buffer, cx| {
3004 buffer.edit(
3005 ranges.iter().map(|range| (range.clone(), text)),
3006 Some(AutoindentMode::EachLine),
3007 cx,
3008 );
3009 });
3010 }
3011
3012 this.refresh_copilot_suggestions(true, cx);
3013 });
3014
3015 let project = self.project.clone()?;
3016 let apply_edits = project.update(cx, |project, cx| {
3017 project.apply_additional_edits_for_completion(
3018 buffer_handle,
3019 completion.clone(),
3020 true,
3021 cx,
3022 )
3023 });
3024 Some(cx.foreground().spawn(async move {
3025 apply_edits.await?;
3026 Ok(())
3027 }))
3028 }
3029
3030 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3031 if matches!(
3032 self.context_menu.as_ref(),
3033 Some(ContextMenu::CodeActions(_))
3034 ) {
3035 self.context_menu.take();
3036 cx.notify();
3037 return;
3038 }
3039
3040 let deployed_from_indicator = action.deployed_from_indicator;
3041 let mut task = self.code_actions_task.take();
3042 cx.spawn(|this, mut cx| async move {
3043 while let Some(prev_task) = task {
3044 prev_task.await;
3045 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3046 }
3047
3048 this.update(&mut cx, |this, cx| {
3049 if this.focused {
3050 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3051 this.show_context_menu(
3052 ContextMenu::CodeActions(CodeActionsMenu {
3053 buffer,
3054 actions,
3055 selected_item: Default::default(),
3056 list: Default::default(),
3057 deployed_from_indicator,
3058 }),
3059 cx,
3060 );
3061 }
3062 }
3063 })?;
3064
3065 Ok::<_, anyhow::Error>(())
3066 })
3067 .detach_and_log_err(cx);
3068 }
3069
3070 pub fn confirm_code_action(
3071 workspace: &mut Workspace,
3072 action: &ConfirmCodeAction,
3073 cx: &mut ViewContext<Workspace>,
3074 ) -> Option<Task<Result<()>>> {
3075 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3076 let actions_menu = if let ContextMenu::CodeActions(menu) =
3077 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3078 {
3079 menu
3080 } else {
3081 return None;
3082 };
3083 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3084 let action = actions_menu.actions.get(action_ix)?.clone();
3085 let title = action.lsp_action.title.clone();
3086 let buffer = actions_menu.buffer;
3087
3088 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3089 project.apply_code_action(buffer, action, true, cx)
3090 });
3091 let editor = editor.downgrade();
3092 Some(cx.spawn(|workspace, cx| async move {
3093 let project_transaction = apply_code_actions.await?;
3094 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3095 }))
3096 }
3097
3098 async fn open_project_transaction(
3099 this: &WeakViewHandle<Editor>,
3100 workspace: WeakViewHandle<Workspace>,
3101 transaction: ProjectTransaction,
3102 title: String,
3103 mut cx: AsyncAppContext,
3104 ) -> Result<()> {
3105 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3106
3107 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3108 entries.sort_unstable_by_key(|(buffer, _)| {
3109 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3110 });
3111
3112 // If the project transaction's edits are all contained within this editor, then
3113 // avoid opening a new editor to display them.
3114
3115 if let Some((buffer, transaction)) = entries.first() {
3116 if entries.len() == 1 {
3117 let excerpt = this.read_with(&cx, |editor, cx| {
3118 editor
3119 .buffer()
3120 .read(cx)
3121 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3122 })?;
3123 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3124 if excerpted_buffer == *buffer {
3125 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3126 let excerpt_range = excerpt_range.to_offset(buffer);
3127 buffer
3128 .edited_ranges_for_transaction::<usize>(transaction)
3129 .all(|range| {
3130 excerpt_range.start <= range.start
3131 && excerpt_range.end >= range.end
3132 })
3133 });
3134
3135 if all_edits_within_excerpt {
3136 return Ok(());
3137 }
3138 }
3139 }
3140 }
3141 } else {
3142 return Ok(());
3143 }
3144
3145 let mut ranges_to_highlight = Vec::new();
3146 let excerpt_buffer = cx.add_model(|cx| {
3147 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3148 for (buffer_handle, transaction) in &entries {
3149 let buffer = buffer_handle.read(cx);
3150 ranges_to_highlight.extend(
3151 multibuffer.push_excerpts_with_context_lines(
3152 buffer_handle.clone(),
3153 buffer
3154 .edited_ranges_for_transaction::<usize>(transaction)
3155 .collect(),
3156 1,
3157 cx,
3158 ),
3159 );
3160 }
3161 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3162 multibuffer
3163 });
3164
3165 workspace.update(&mut cx, |workspace, cx| {
3166 let project = workspace.project().clone();
3167 let editor =
3168 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3169 workspace.add_item(Box::new(editor.clone()), cx);
3170 editor.update(cx, |editor, cx| {
3171 editor.highlight_background::<Self>(
3172 ranges_to_highlight,
3173 |theme| theme.editor.highlighted_line_background,
3174 cx,
3175 );
3176 });
3177 })?;
3178
3179 Ok(())
3180 }
3181
3182 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3183 let project = self.project.as_ref()?;
3184 let buffer = self.buffer.read(cx);
3185 let newest_selection = self.selections.newest_anchor().clone();
3186 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3187 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3188 if start_buffer != end_buffer {
3189 return None;
3190 }
3191
3192 let actions = project.update(cx, |project, cx| {
3193 project.code_actions(&start_buffer, start..end, cx)
3194 });
3195 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3196 let actions = actions.await;
3197 this.update(&mut cx, |this, cx| {
3198 this.available_code_actions = actions.log_err().and_then(|actions| {
3199 if actions.is_empty() {
3200 None
3201 } else {
3202 Some((start_buffer, actions.into()))
3203 }
3204 });
3205 cx.notify();
3206 })
3207 .log_err();
3208 }));
3209 None
3210 }
3211
3212 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3213 if self.pending_rename.is_some() {
3214 return None;
3215 }
3216
3217 let project = self.project.as_ref()?;
3218 let buffer = self.buffer.read(cx);
3219 let newest_selection = self.selections.newest_anchor().clone();
3220 let cursor_position = newest_selection.head();
3221 let (cursor_buffer, cursor_buffer_position) =
3222 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3223 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3224 if cursor_buffer != tail_buffer {
3225 return None;
3226 }
3227
3228 let highlights = project.update(cx, |project, cx| {
3229 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3230 });
3231
3232 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3233 if let Some(highlights) = highlights.await.log_err() {
3234 this.update(&mut cx, |this, cx| {
3235 if this.pending_rename.is_some() {
3236 return;
3237 }
3238
3239 let buffer_id = cursor_position.buffer_id;
3240 let buffer = this.buffer.read(cx);
3241 if !buffer
3242 .text_anchor_for_position(cursor_position, cx)
3243 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
3244 {
3245 return;
3246 }
3247
3248 let cursor_buffer_snapshot = cursor_buffer.read(cx);
3249 let mut write_ranges = Vec::new();
3250 let mut read_ranges = Vec::new();
3251 for highlight in highlights {
3252 for (excerpt_id, excerpt_range) in
3253 buffer.excerpts_for_buffer(&cursor_buffer, cx)
3254 {
3255 let start = highlight
3256 .range
3257 .start
3258 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
3259 let end = highlight
3260 .range
3261 .end
3262 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
3263 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
3264 continue;
3265 }
3266
3267 let range = Anchor {
3268 buffer_id,
3269 excerpt_id: excerpt_id.clone(),
3270 text_anchor: start,
3271 }..Anchor {
3272 buffer_id,
3273 excerpt_id,
3274 text_anchor: end,
3275 };
3276 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
3277 write_ranges.push(range);
3278 } else {
3279 read_ranges.push(range);
3280 }
3281 }
3282 }
3283
3284 this.highlight_background::<DocumentHighlightRead>(
3285 read_ranges,
3286 |theme| theme.editor.document_highlight_read_background,
3287 cx,
3288 );
3289 this.highlight_background::<DocumentHighlightWrite>(
3290 write_ranges,
3291 |theme| theme.editor.document_highlight_write_background,
3292 cx,
3293 );
3294 cx.notify();
3295 })
3296 .log_err();
3297 }
3298 }));
3299 None
3300 }
3301
3302 fn refresh_copilot_suggestions(
3303 &mut self,
3304 debounce: bool,
3305 cx: &mut ViewContext<Self>,
3306 ) -> Option<()> {
3307 let copilot = Copilot::global(cx)?;
3308 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3309 self.clear_copilot_suggestions(cx);
3310 return None;
3311 }
3312 self.update_visible_copilot_suggestion(cx);
3313
3314 let snapshot = self.buffer.read(cx).snapshot(cx);
3315 let cursor = self.selections.newest_anchor().head();
3316 if !self.is_copilot_enabled_at(cursor, &snapshot, cx) {
3317 self.clear_copilot_suggestions(cx);
3318 return None;
3319 }
3320
3321 let (buffer, buffer_position) =
3322 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3323 self.copilot_state.pending_refresh = cx.spawn(|this, mut cx| async move {
3324 if debounce {
3325 cx.background().timer(COPILOT_DEBOUNCE_TIMEOUT).await;
3326 }
3327
3328 let completions = copilot
3329 .update(&mut cx, |copilot, cx| {
3330 copilot.completions(&buffer, buffer_position, cx)
3331 })
3332 .await
3333 .log_err()
3334 .into_iter()
3335 .flatten()
3336 .collect_vec();
3337
3338 this.update(&mut cx, |this, cx| {
3339 if !completions.is_empty() {
3340 this.copilot_state.cycled = false;
3341 this.copilot_state.pending_cycling_refresh = Task::ready(None);
3342 this.copilot_state.completions.clear();
3343 this.copilot_state.active_completion_index = 0;
3344 this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
3345 for completion in completions {
3346 this.copilot_state.push_completion(completion);
3347 }
3348 this.update_visible_copilot_suggestion(cx);
3349 }
3350 })
3351 .log_err()?;
3352 Some(())
3353 });
3354
3355 Some(())
3356 }
3357
3358 fn cycle_copilot_suggestions(
3359 &mut self,
3360 direction: Direction,
3361 cx: &mut ViewContext<Self>,
3362 ) -> Option<()> {
3363 let copilot = Copilot::global(cx)?;
3364 if self.mode != EditorMode::Full || !copilot.read(cx).status().is_authorized() {
3365 return None;
3366 }
3367
3368 if self.copilot_state.cycled {
3369 self.copilot_state.cycle_completions(direction);
3370 self.update_visible_copilot_suggestion(cx);
3371 } else {
3372 let cursor = self.selections.newest_anchor().head();
3373 let (buffer, buffer_position) =
3374 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
3375 self.copilot_state.pending_cycling_refresh = cx.spawn(|this, mut cx| async move {
3376 let completions = copilot
3377 .update(&mut cx, |copilot, cx| {
3378 copilot.completions_cycling(&buffer, buffer_position, cx)
3379 })
3380 .await;
3381
3382 this.update(&mut cx, |this, cx| {
3383 this.copilot_state.cycled = true;
3384 for completion in completions.log_err().into_iter().flatten() {
3385 this.copilot_state.push_completion(completion);
3386 }
3387 this.copilot_state.cycle_completions(direction);
3388 this.update_visible_copilot_suggestion(cx);
3389 })
3390 .log_err()?;
3391
3392 Some(())
3393 });
3394 }
3395
3396 Some(())
3397 }
3398
3399 fn copilot_suggest(&mut self, _: &copilot::Suggest, cx: &mut ViewContext<Self>) {
3400 if !self.has_active_copilot_suggestion(cx) {
3401 self.refresh_copilot_suggestions(false, cx);
3402 return;
3403 }
3404
3405 self.update_visible_copilot_suggestion(cx);
3406 }
3407
3408 fn next_copilot_suggestion(&mut self, _: &copilot::NextSuggestion, cx: &mut ViewContext<Self>) {
3409 if self.has_active_copilot_suggestion(cx) {
3410 self.cycle_copilot_suggestions(Direction::Next, cx);
3411 } else {
3412 self.refresh_copilot_suggestions(false, cx);
3413 }
3414 }
3415
3416 fn previous_copilot_suggestion(
3417 &mut self,
3418 _: &copilot::PreviousSuggestion,
3419 cx: &mut ViewContext<Self>,
3420 ) {
3421 if self.has_active_copilot_suggestion(cx) {
3422 self.cycle_copilot_suggestions(Direction::Prev, cx);
3423 } else {
3424 self.refresh_copilot_suggestions(false, cx);
3425 }
3426 }
3427
3428 fn accept_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3429 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3430 if let Some((copilot, completion)) =
3431 Copilot::global(cx).zip(self.copilot_state.active_completion())
3432 {
3433 copilot
3434 .update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
3435 .detach_and_log_err(cx);
3436
3437 self.report_copilot_event(Some(completion.uuid.clone()), true, cx)
3438 }
3439 self.insert_with_autoindent_mode(&suggestion.text.to_string(), None, cx);
3440 cx.notify();
3441 true
3442 } else {
3443 false
3444 }
3445 }
3446
3447 fn discard_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> bool {
3448 if let Some(suggestion) = self.take_active_copilot_suggestion(cx) {
3449 if let Some(copilot) = Copilot::global(cx) {
3450 copilot
3451 .update(cx, |copilot, cx| {
3452 copilot.discard_completions(&self.copilot_state.completions, cx)
3453 })
3454 .detach_and_log_err(cx);
3455
3456 self.report_copilot_event(None, false, cx)
3457 }
3458
3459 self.display_map.update(cx, |map, cx| {
3460 map.splice_inlays(vec![suggestion.id], Vec::new(), cx)
3461 });
3462 cx.notify();
3463 true
3464 } else {
3465 false
3466 }
3467 }
3468
3469 fn is_copilot_enabled_at(
3470 &self,
3471 location: Anchor,
3472 snapshot: &MultiBufferSnapshot,
3473 cx: &mut ViewContext<Self>,
3474 ) -> bool {
3475 let file = snapshot.file_at(location);
3476 let language = snapshot.language_at(location);
3477 let settings = all_language_settings(file, cx);
3478 settings.copilot_enabled(language, file.map(|f| f.path().as_ref()))
3479 }
3480
3481 fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
3482 if let Some(suggestion) = self.copilot_state.suggestion.as_ref() {
3483 let buffer = self.buffer.read(cx).read(cx);
3484 suggestion.position.is_valid(&buffer)
3485 } else {
3486 false
3487 }
3488 }
3489
3490 fn take_active_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) -> Option<Inlay> {
3491 let suggestion = self.copilot_state.suggestion.take()?;
3492 self.display_map.update(cx, |map, cx| {
3493 map.splice_inlays(vec![suggestion.id], Default::default(), cx);
3494 });
3495 let buffer = self.buffer.read(cx).read(cx);
3496
3497 if suggestion.position.is_valid(&buffer) {
3498 Some(suggestion)
3499 } else {
3500 None
3501 }
3502 }
3503
3504 fn update_visible_copilot_suggestion(&mut self, cx: &mut ViewContext<Self>) {
3505 let snapshot = self.buffer.read(cx).snapshot(cx);
3506 let selection = self.selections.newest_anchor();
3507 let cursor = selection.head();
3508
3509 if self.context_menu.is_some()
3510 || !self.completion_tasks.is_empty()
3511 || selection.start != selection.end
3512 {
3513 self.discard_copilot_suggestion(cx);
3514 } else if let Some(text) = self
3515 .copilot_state
3516 .text_for_active_completion(cursor, &snapshot)
3517 {
3518 let text = Rope::from(text);
3519 let mut to_remove = Vec::new();
3520 if let Some(suggestion) = self.copilot_state.suggestion.take() {
3521 to_remove.push(suggestion.id);
3522 }
3523
3524 let suggestion_inlay =
3525 Inlay::suggestion(post_inc(&mut self.next_inlay_id), cursor, text);
3526 self.copilot_state.suggestion = Some(suggestion_inlay.clone());
3527 self.display_map.update(cx, move |map, cx| {
3528 map.splice_inlays(to_remove, vec![suggestion_inlay], cx)
3529 });
3530 cx.notify();
3531 } else {
3532 self.discard_copilot_suggestion(cx);
3533 }
3534 }
3535
3536 fn clear_copilot_suggestions(&mut self, cx: &mut ViewContext<Self>) {
3537 self.copilot_state = Default::default();
3538 self.discard_copilot_suggestion(cx);
3539 }
3540
3541 pub fn render_code_actions_indicator(
3542 &self,
3543 style: &EditorStyle,
3544 is_active: bool,
3545 cx: &mut ViewContext<Self>,
3546 ) -> Option<AnyElement<Self>> {
3547 if self.available_code_actions.is_some() {
3548 enum CodeActions {}
3549 Some(
3550 MouseEventHandler::<CodeActions, _>::new(0, cx, |state, _| {
3551 Svg::new("icons/bolt_8.svg").with_color(
3552 style
3553 .code_actions
3554 .indicator
3555 .in_state(is_active)
3556 .style_for(state)
3557 .color,
3558 )
3559 })
3560 .with_cursor_style(CursorStyle::PointingHand)
3561 .with_padding(Padding::uniform(3.))
3562 .on_down(MouseButton::Left, |_, this, cx| {
3563 this.toggle_code_actions(
3564 &ToggleCodeActions {
3565 deployed_from_indicator: true,
3566 },
3567 cx,
3568 );
3569 })
3570 .into_any(),
3571 )
3572 } else {
3573 None
3574 }
3575 }
3576
3577 pub fn render_fold_indicators(
3578 &self,
3579 fold_data: Vec<Option<(FoldStatus, u32, bool)>>,
3580 style: &EditorStyle,
3581 gutter_hovered: bool,
3582 line_height: f32,
3583 gutter_margin: f32,
3584 cx: &mut ViewContext<Self>,
3585 ) -> Vec<Option<AnyElement<Self>>> {
3586 enum FoldIndicators {}
3587
3588 let style = style.folds.clone();
3589
3590 fold_data
3591 .iter()
3592 .enumerate()
3593 .map(|(ix, fold_data)| {
3594 fold_data
3595 .map(|(fold_status, buffer_row, active)| {
3596 (active || gutter_hovered || fold_status == FoldStatus::Folded).then(|| {
3597 MouseEventHandler::<FoldIndicators, _>::new(
3598 ix as usize,
3599 cx,
3600 |mouse_state, _| {
3601 Svg::new(match fold_status {
3602 FoldStatus::Folded => style.folded_icon.clone(),
3603 FoldStatus::Foldable => style.foldable_icon.clone(),
3604 })
3605 .with_color(
3606 style
3607 .indicator
3608 .in_state(fold_status == FoldStatus::Folded)
3609 .style_for(mouse_state)
3610 .color,
3611 )
3612 .constrained()
3613 .with_width(gutter_margin * style.icon_margin_scale)
3614 .aligned()
3615 .constrained()
3616 .with_height(line_height)
3617 .with_width(gutter_margin)
3618 .aligned()
3619 },
3620 )
3621 .with_cursor_style(CursorStyle::PointingHand)
3622 .with_padding(Padding::uniform(3.))
3623 .on_click(MouseButton::Left, {
3624 move |_, editor, cx| match fold_status {
3625 FoldStatus::Folded => {
3626 editor.unfold_at(&UnfoldAt { buffer_row }, cx);
3627 }
3628 FoldStatus::Foldable => {
3629 editor.fold_at(&FoldAt { buffer_row }, cx);
3630 }
3631 }
3632 })
3633 .into_any()
3634 })
3635 })
3636 .flatten()
3637 })
3638 .collect()
3639 }
3640
3641 pub fn context_menu_visible(&self) -> bool {
3642 self.context_menu
3643 .as_ref()
3644 .map_or(false, |menu| menu.visible())
3645 }
3646
3647 pub fn render_context_menu(
3648 &self,
3649 cursor_position: DisplayPoint,
3650 style: EditorStyle,
3651 cx: &mut ViewContext<Editor>,
3652 ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
3653 self.context_menu
3654 .as_ref()
3655 .map(|menu| menu.render(cursor_position, style, cx))
3656 }
3657
3658 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
3659 if !matches!(menu, ContextMenu::Completions(_)) {
3660 self.completion_tasks.clear();
3661 }
3662 self.context_menu = Some(menu);
3663 self.discard_copilot_suggestion(cx);
3664 cx.notify();
3665 }
3666
3667 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
3668 cx.notify();
3669 self.completion_tasks.clear();
3670 let context_menu = self.context_menu.take();
3671 if context_menu.is_some() {
3672 self.update_visible_copilot_suggestion(cx);
3673 }
3674 context_menu
3675 }
3676
3677 pub fn insert_snippet(
3678 &mut self,
3679 insertion_ranges: &[Range<usize>],
3680 snippet: Snippet,
3681 cx: &mut ViewContext<Self>,
3682 ) -> Result<()> {
3683 let tabstops = self.buffer.update(cx, |buffer, cx| {
3684 let snippet_text: Arc<str> = snippet.text.clone().into();
3685 buffer.edit(
3686 insertion_ranges
3687 .iter()
3688 .cloned()
3689 .map(|range| (range, snippet_text.clone())),
3690 Some(AutoindentMode::EachLine),
3691 cx,
3692 );
3693
3694 let snapshot = &*buffer.read(cx);
3695 let snippet = &snippet;
3696 snippet
3697 .tabstops
3698 .iter()
3699 .map(|tabstop| {
3700 let mut tabstop_ranges = tabstop
3701 .iter()
3702 .flat_map(|tabstop_range| {
3703 let mut delta = 0_isize;
3704 insertion_ranges.iter().map(move |insertion_range| {
3705 let insertion_start = insertion_range.start as isize + delta;
3706 delta +=
3707 snippet.text.len() as isize - insertion_range.len() as isize;
3708
3709 let start = snapshot.anchor_before(
3710 (insertion_start + tabstop_range.start) as usize,
3711 );
3712 let end = snapshot
3713 .anchor_after((insertion_start + tabstop_range.end) as usize);
3714 start..end
3715 })
3716 })
3717 .collect::<Vec<_>>();
3718 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
3719 tabstop_ranges
3720 })
3721 .collect::<Vec<_>>()
3722 });
3723
3724 if let Some(tabstop) = tabstops.first() {
3725 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3726 s.select_ranges(tabstop.iter().cloned());
3727 });
3728 self.snippet_stack.push(SnippetState {
3729 active_index: 0,
3730 ranges: tabstops,
3731 });
3732 }
3733
3734 Ok(())
3735 }
3736
3737 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3738 self.move_to_snippet_tabstop(Bias::Right, cx)
3739 }
3740
3741 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
3742 self.move_to_snippet_tabstop(Bias::Left, cx)
3743 }
3744
3745 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
3746 if let Some(mut snippet) = self.snippet_stack.pop() {
3747 match bias {
3748 Bias::Left => {
3749 if snippet.active_index > 0 {
3750 snippet.active_index -= 1;
3751 } else {
3752 self.snippet_stack.push(snippet);
3753 return false;
3754 }
3755 }
3756 Bias::Right => {
3757 if snippet.active_index + 1 < snippet.ranges.len() {
3758 snippet.active_index += 1;
3759 } else {
3760 self.snippet_stack.push(snippet);
3761 return false;
3762 }
3763 }
3764 }
3765 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
3766 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
3767 s.select_anchor_ranges(current_ranges.iter().cloned())
3768 });
3769 // If snippet state is not at the last tabstop, push it back on the stack
3770 if snippet.active_index + 1 < snippet.ranges.len() {
3771 self.snippet_stack.push(snippet);
3772 }
3773 return true;
3774 }
3775 }
3776
3777 false
3778 }
3779
3780 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
3781 self.transact(cx, |this, cx| {
3782 this.select_all(&SelectAll, cx);
3783 this.insert("", cx);
3784 });
3785 }
3786
3787 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
3788 self.transact(cx, |this, cx| {
3789 this.select_autoclose_pair(cx);
3790 let mut selections = this.selections.all::<Point>(cx);
3791 if !this.selections.line_mode {
3792 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3793 for selection in &mut selections {
3794 if selection.is_empty() {
3795 let old_head = selection.head();
3796 let mut new_head =
3797 movement::left(&display_map, old_head.to_display_point(&display_map))
3798 .to_point(&display_map);
3799 if let Some((buffer, line_buffer_range)) = display_map
3800 .buffer_snapshot
3801 .buffer_line_for_row(old_head.row)
3802 {
3803 let indent_size =
3804 buffer.indent_size_for_line(line_buffer_range.start.row);
3805 let indent_len = match indent_size.kind {
3806 IndentKind::Space => {
3807 buffer.settings_at(line_buffer_range.start, cx).tab_size
3808 }
3809 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
3810 };
3811 if old_head.column <= indent_size.len && old_head.column > 0 {
3812 let indent_len = indent_len.get();
3813 new_head = cmp::min(
3814 new_head,
3815 Point::new(
3816 old_head.row,
3817 ((old_head.column - 1) / indent_len) * indent_len,
3818 ),
3819 );
3820 }
3821 }
3822
3823 selection.set_head(new_head, SelectionGoal::None);
3824 }
3825 }
3826 }
3827
3828 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3829 this.insert("", cx);
3830 this.refresh_copilot_suggestions(true, cx);
3831 });
3832 }
3833
3834 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
3835 self.transact(cx, |this, cx| {
3836 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
3837 let line_mode = s.line_mode;
3838 s.move_with(|map, selection| {
3839 if selection.is_empty() && !line_mode {
3840 let cursor = movement::right(map, selection.head());
3841 selection.end = cursor;
3842 selection.reversed = true;
3843 selection.goal = SelectionGoal::None;
3844 }
3845 })
3846 });
3847 this.insert("", cx);
3848 this.refresh_copilot_suggestions(true, cx);
3849 });
3850 }
3851
3852 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
3853 if self.move_to_prev_snippet_tabstop(cx) {
3854 return;
3855 }
3856
3857 self.outdent(&Outdent, cx);
3858 }
3859
3860 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
3861 if self.move_to_next_snippet_tabstop(cx) {
3862 return;
3863 }
3864
3865 let mut selections = self.selections.all_adjusted(cx);
3866 let buffer = self.buffer.read(cx);
3867 let snapshot = buffer.snapshot(cx);
3868 let rows_iter = selections.iter().map(|s| s.head().row);
3869 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
3870
3871 let mut edits = Vec::new();
3872 let mut prev_edited_row = 0;
3873 let mut row_delta = 0;
3874 for selection in &mut selections {
3875 if selection.start.row != prev_edited_row {
3876 row_delta = 0;
3877 }
3878 prev_edited_row = selection.end.row;
3879
3880 // If the selection is non-empty, then increase the indentation of the selected lines.
3881 if !selection.is_empty() {
3882 row_delta =
3883 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3884 continue;
3885 }
3886
3887 // If the selection is empty and the cursor is in the leading whitespace before the
3888 // suggested indentation, then auto-indent the line.
3889 let cursor = selection.head();
3890 let current_indent = snapshot.indent_size_for_line(cursor.row);
3891 if let Some(suggested_indent) = suggested_indents.get(&cursor.row).copied() {
3892 if cursor.column < suggested_indent.len
3893 && cursor.column <= current_indent.len
3894 && current_indent.len <= suggested_indent.len
3895 {
3896 selection.start = Point::new(cursor.row, suggested_indent.len);
3897 selection.end = selection.start;
3898 if row_delta == 0 {
3899 edits.extend(Buffer::edit_for_indent_size_adjustment(
3900 cursor.row,
3901 current_indent,
3902 suggested_indent,
3903 ));
3904 row_delta = suggested_indent.len - current_indent.len;
3905 }
3906 continue;
3907 }
3908 }
3909
3910 // Accept copilot suggestion if there is only one selection and the cursor is not
3911 // in the leading whitespace.
3912 if self.selections.count() == 1
3913 && cursor.column >= current_indent.len
3914 && self.has_active_copilot_suggestion(cx)
3915 {
3916 self.accept_copilot_suggestion(cx);
3917 return;
3918 }
3919
3920 // Otherwise, insert a hard or soft tab.
3921 let settings = buffer.settings_at(cursor, cx);
3922 let tab_size = if settings.hard_tabs {
3923 IndentSize::tab()
3924 } else {
3925 let tab_size = settings.tab_size.get();
3926 let char_column = snapshot
3927 .text_for_range(Point::new(cursor.row, 0)..cursor)
3928 .flat_map(str::chars)
3929 .count()
3930 + row_delta as usize;
3931 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
3932 IndentSize::spaces(chars_to_next_tab_stop)
3933 };
3934 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
3935 selection.end = selection.start;
3936 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
3937 row_delta += tab_size.len;
3938 }
3939
3940 self.transact(cx, |this, cx| {
3941 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3942 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3943 this.refresh_copilot_suggestions(true, cx);
3944 });
3945 }
3946
3947 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
3948 let mut selections = self.selections.all::<Point>(cx);
3949 let mut prev_edited_row = 0;
3950 let mut row_delta = 0;
3951 let mut edits = Vec::new();
3952 let buffer = self.buffer.read(cx);
3953 let snapshot = buffer.snapshot(cx);
3954 for selection in &mut selections {
3955 if selection.start.row != prev_edited_row {
3956 row_delta = 0;
3957 }
3958 prev_edited_row = selection.end.row;
3959
3960 row_delta =
3961 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
3962 }
3963
3964 self.transact(cx, |this, cx| {
3965 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
3966 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
3967 });
3968 }
3969
3970 fn indent_selection(
3971 buffer: &MultiBuffer,
3972 snapshot: &MultiBufferSnapshot,
3973 selection: &mut Selection<Point>,
3974 edits: &mut Vec<(Range<Point>, String)>,
3975 delta_for_start_row: u32,
3976 cx: &AppContext,
3977 ) -> u32 {
3978 let settings = buffer.settings_at(selection.start, cx);
3979 let tab_size = settings.tab_size.get();
3980 let indent_kind = if settings.hard_tabs {
3981 IndentKind::Tab
3982 } else {
3983 IndentKind::Space
3984 };
3985 let mut start_row = selection.start.row;
3986 let mut end_row = selection.end.row + 1;
3987
3988 // If a selection ends at the beginning of a line, don't indent
3989 // that last line.
3990 if selection.end.column == 0 {
3991 end_row -= 1;
3992 }
3993
3994 // Avoid re-indenting a row that has already been indented by a
3995 // previous selection, but still update this selection's column
3996 // to reflect that indentation.
3997 if delta_for_start_row > 0 {
3998 start_row += 1;
3999 selection.start.column += delta_for_start_row;
4000 if selection.end.row == selection.start.row {
4001 selection.end.column += delta_for_start_row;
4002 }
4003 }
4004
4005 let mut delta_for_end_row = 0;
4006 for row in start_row..end_row {
4007 let current_indent = snapshot.indent_size_for_line(row);
4008 let indent_delta = match (current_indent.kind, indent_kind) {
4009 (IndentKind::Space, IndentKind::Space) => {
4010 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
4011 IndentSize::spaces(columns_to_next_tab_stop)
4012 }
4013 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
4014 (_, IndentKind::Tab) => IndentSize::tab(),
4015 };
4016
4017 let row_start = Point::new(row, 0);
4018 edits.push((
4019 row_start..row_start,
4020 indent_delta.chars().collect::<String>(),
4021 ));
4022
4023 // Update this selection's endpoints to reflect the indentation.
4024 if row == selection.start.row {
4025 selection.start.column += indent_delta.len;
4026 }
4027 if row == selection.end.row {
4028 selection.end.column += indent_delta.len;
4029 delta_for_end_row = indent_delta.len;
4030 }
4031 }
4032
4033 if selection.start.row == selection.end.row {
4034 delta_for_start_row + delta_for_end_row
4035 } else {
4036 delta_for_end_row
4037 }
4038 }
4039
4040 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
4041 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4042 let selections = self.selections.all::<Point>(cx);
4043 let mut deletion_ranges = Vec::new();
4044 let mut last_outdent = None;
4045 {
4046 let buffer = self.buffer.read(cx);
4047 let snapshot = buffer.snapshot(cx);
4048 for selection in &selections {
4049 let settings = buffer.settings_at(selection.start, cx);
4050 let tab_size = settings.tab_size.get();
4051 let mut rows = selection.spanned_rows(false, &display_map);
4052
4053 // Avoid re-outdenting a row that has already been outdented by a
4054 // previous selection.
4055 if let Some(last_row) = last_outdent {
4056 if last_row == rows.start {
4057 rows.start += 1;
4058 }
4059 }
4060
4061 for row in rows {
4062 let indent_size = snapshot.indent_size_for_line(row);
4063 if indent_size.len > 0 {
4064 let deletion_len = match indent_size.kind {
4065 IndentKind::Space => {
4066 let columns_to_prev_tab_stop = indent_size.len % tab_size;
4067 if columns_to_prev_tab_stop == 0 {
4068 tab_size
4069 } else {
4070 columns_to_prev_tab_stop
4071 }
4072 }
4073 IndentKind::Tab => 1,
4074 };
4075 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
4076 last_outdent = Some(row);
4077 }
4078 }
4079 }
4080 }
4081
4082 self.transact(cx, |this, cx| {
4083 this.buffer.update(cx, |buffer, cx| {
4084 let empty_str: Arc<str> = "".into();
4085 buffer.edit(
4086 deletion_ranges
4087 .into_iter()
4088 .map(|range| (range, empty_str.clone())),
4089 None,
4090 cx,
4091 );
4092 });
4093 let selections = this.selections.all::<usize>(cx);
4094 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
4095 });
4096 }
4097
4098 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
4099 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4100 let selections = self.selections.all::<Point>(cx);
4101
4102 let mut new_cursors = Vec::new();
4103 let mut edit_ranges = Vec::new();
4104 let mut selections = selections.iter().peekable();
4105 while let Some(selection) = selections.next() {
4106 let mut rows = selection.spanned_rows(false, &display_map);
4107 let goal_display_column = selection.head().to_display_point(&display_map).column();
4108
4109 // Accumulate contiguous regions of rows that we want to delete.
4110 while let Some(next_selection) = selections.peek() {
4111 let next_rows = next_selection.spanned_rows(false, &display_map);
4112 if next_rows.start <= rows.end {
4113 rows.end = next_rows.end;
4114 selections.next().unwrap();
4115 } else {
4116 break;
4117 }
4118 }
4119
4120 let buffer = &display_map.buffer_snapshot;
4121 let mut edit_start = Point::new(rows.start, 0).to_offset(buffer);
4122 let edit_end;
4123 let cursor_buffer_row;
4124 if buffer.max_point().row >= rows.end {
4125 // If there's a line after the range, delete the \n from the end of the row range
4126 // and position the cursor on the next line.
4127 edit_end = Point::new(rows.end, 0).to_offset(buffer);
4128 cursor_buffer_row = rows.end;
4129 } else {
4130 // If there isn't a line after the range, delete the \n from the line before the
4131 // start of the row range and position the cursor there.
4132 edit_start = edit_start.saturating_sub(1);
4133 edit_end = buffer.len();
4134 cursor_buffer_row = rows.start.saturating_sub(1);
4135 }
4136
4137 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
4138 *cursor.column_mut() =
4139 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
4140
4141 new_cursors.push((
4142 selection.id,
4143 buffer.anchor_after(cursor.to_point(&display_map)),
4144 ));
4145 edit_ranges.push(edit_start..edit_end);
4146 }
4147
4148 self.transact(cx, |this, cx| {
4149 let buffer = this.buffer.update(cx, |buffer, cx| {
4150 let empty_str: Arc<str> = "".into();
4151 buffer.edit(
4152 edit_ranges
4153 .into_iter()
4154 .map(|range| (range, empty_str.clone())),
4155 None,
4156 cx,
4157 );
4158 buffer.snapshot(cx)
4159 });
4160 let new_selections = new_cursors
4161 .into_iter()
4162 .map(|(id, cursor)| {
4163 let cursor = cursor.to_point(&buffer);
4164 Selection {
4165 id,
4166 start: cursor,
4167 end: cursor,
4168 reversed: false,
4169 goal: SelectionGoal::None,
4170 }
4171 })
4172 .collect();
4173
4174 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4175 s.select(new_selections);
4176 });
4177 });
4178 }
4179
4180 pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
4181 let mut row_ranges = Vec::<Range<u32>>::new();
4182 for selection in self.selections.all::<Point>(cx) {
4183 let start = selection.start.row;
4184 let end = if selection.start.row == selection.end.row {
4185 selection.start.row + 1
4186 } else {
4187 selection.end.row
4188 };
4189
4190 if let Some(last_row_range) = row_ranges.last_mut() {
4191 if start <= last_row_range.end {
4192 last_row_range.end = end;
4193 continue;
4194 }
4195 }
4196 row_ranges.push(start..end);
4197 }
4198
4199 let snapshot = self.buffer.read(cx).snapshot(cx);
4200 let mut cursor_positions = Vec::new();
4201 for row_range in &row_ranges {
4202 let anchor = snapshot.anchor_before(Point::new(
4203 row_range.end - 1,
4204 snapshot.line_len(row_range.end - 1),
4205 ));
4206 cursor_positions.push(anchor.clone()..anchor);
4207 }
4208
4209 self.transact(cx, |this, cx| {
4210 for row_range in row_ranges.into_iter().rev() {
4211 for row in row_range.rev() {
4212 let end_of_line = Point::new(row, snapshot.line_len(row));
4213 let indent = snapshot.indent_size_for_line(row + 1);
4214 let start_of_next_line = Point::new(row + 1, indent.len);
4215
4216 let replace = if snapshot.line_len(row + 1) > indent.len {
4217 " "
4218 } else {
4219 ""
4220 };
4221
4222 this.buffer.update(cx, |buffer, cx| {
4223 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
4224 });
4225 }
4226 }
4227
4228 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4229 s.select_anchor_ranges(cursor_positions)
4230 });
4231 });
4232 }
4233
4234 pub fn sort_lines_case_sensitive(
4235 &mut self,
4236 _: &SortLinesCaseSensitive,
4237 cx: &mut ViewContext<Self>,
4238 ) {
4239 self.manipulate_lines(cx, |lines| lines.sort())
4240 }
4241
4242 pub fn sort_lines_case_insensitive(
4243 &mut self,
4244 _: &SortLinesCaseInsensitive,
4245 cx: &mut ViewContext<Self>,
4246 ) {
4247 self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
4248 }
4249
4250 pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
4251 self.manipulate_lines(cx, |lines| lines.reverse())
4252 }
4253
4254 pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext<Self>) {
4255 self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng()))
4256 }
4257
4258 fn manipulate_lines<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4259 where
4260 Fn: FnMut(&mut [&str]),
4261 {
4262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4263 let buffer = self.buffer.read(cx).snapshot(cx);
4264
4265 let mut edits = Vec::new();
4266
4267 let selections = self.selections.all::<Point>(cx);
4268 let mut selections = selections.iter().peekable();
4269 let mut contiguous_row_selections = Vec::new();
4270 let mut new_selections = Vec::new();
4271
4272 while let Some(selection) = selections.next() {
4273 let (start_row, end_row) = consume_contiguous_rows(
4274 &mut contiguous_row_selections,
4275 selection,
4276 &display_map,
4277 &mut selections,
4278 );
4279
4280 let start_point = Point::new(start_row, 0);
4281 let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1));
4282 let text = buffer
4283 .text_for_range(start_point..end_point)
4284 .collect::<String>();
4285 let mut lines = text.split("\n").collect_vec();
4286
4287 let lines_len = lines.len();
4288 callback(&mut lines);
4289
4290 // This is a current limitation with selections.
4291 // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
4292 debug_assert!(
4293 lines.len() == lines_len,
4294 "callback should not change the number of lines"
4295 );
4296
4297 edits.push((start_point..end_point, lines.join("\n")));
4298 let start_anchor = buffer.anchor_after(start_point);
4299 let end_anchor = buffer.anchor_before(end_point);
4300
4301 // Make selection and push
4302 new_selections.push(Selection {
4303 id: selection.id,
4304 start: start_anchor.to_offset(&buffer),
4305 end: end_anchor.to_offset(&buffer),
4306 goal: SelectionGoal::None,
4307 reversed: selection.reversed,
4308 });
4309 }
4310
4311 self.transact(cx, |this, cx| {
4312 this.buffer.update(cx, |buffer, cx| {
4313 buffer.edit(edits, None, cx);
4314 });
4315
4316 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
4317 s.select(new_selections);
4318 });
4319
4320 this.request_autoscroll(Autoscroll::fit(), cx);
4321 });
4322 }
4323
4324 pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
4325 self.manipulate_text(cx, |text| text.to_uppercase())
4326 }
4327
4328 pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
4329 self.manipulate_text(cx, |text| text.to_lowercase())
4330 }
4331
4332 pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
4333 self.manipulate_text(cx, |text| text.to_case(Case::Title))
4334 }
4335
4336 pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
4337 self.manipulate_text(cx, |text| text.to_case(Case::Snake))
4338 }
4339
4340 pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
4341 self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
4342 }
4343
4344 pub fn convert_to_upper_camel_case(
4345 &mut self,
4346 _: &ConvertToUpperCamelCase,
4347 cx: &mut ViewContext<Self>,
4348 ) {
4349 self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel))
4350 }
4351
4352 pub fn convert_to_lower_camel_case(
4353 &mut self,
4354 _: &ConvertToLowerCamelCase,
4355 cx: &mut ViewContext<Self>,
4356 ) {
4357 self.manipulate_text(cx, |text| text.to_case(Case::Camel))
4358 }
4359
4360 fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
4361 where
4362 Fn: FnMut(&str) -> String,
4363 {
4364 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4365 let buffer = self.buffer.read(cx).snapshot(cx);
4366
4367 let mut new_selections = Vec::new();
4368 let mut edits = Vec::new();
4369 for selection in self.selections.all::<usize>(cx) {
4370 let selection_is_empty = selection.is_empty();
4371
4372 let (start, end) = if selection_is_empty {
4373 let word_range = movement::surrounding_word(
4374 &display_map,
4375 selection.start.to_display_point(&display_map),
4376 );
4377 let start = word_range.start.to_offset(&display_map, Bias::Left);
4378 let end = word_range.end.to_offset(&display_map, Bias::Left);
4379 (start, end)
4380 } else {
4381 (selection.start, selection.end)
4382 };
4383
4384 let text = buffer.text_for_range(start..end).collect::<String>();
4385 let text = callback(&text);
4386
4387 if selection_is_empty {
4388 new_selections.push(selection);
4389 } else {
4390 new_selections.push(Selection {
4391 start,
4392 end: start + text.len(),
4393 goal: SelectionGoal::None,
4394 ..selection
4395 });
4396 }
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 highlight_text<T: 'static>(
7552 &mut self,
7553 ranges: Vec<Range<Anchor>>,
7554 style: HighlightStyle,
7555 cx: &mut ViewContext<Self>,
7556 ) {
7557 self.display_map.update(cx, |map, _| {
7558 map.highlight_text(TypeId::of::<T>(), ranges, style)
7559 });
7560 cx.notify();
7561 }
7562
7563 pub fn text_highlights<'a, T: 'static>(
7564 &'a self,
7565 cx: &'a AppContext,
7566 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7567 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7568 }
7569
7570 pub fn clear_text_highlights<T: 'static>(
7571 &mut self,
7572 cx: &mut ViewContext<Self>,
7573 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7574 let highlights = self
7575 .display_map
7576 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7577 if highlights.is_some() {
7578 cx.notify();
7579 }
7580 highlights
7581 }
7582
7583 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7584 self.blink_manager.read(cx).visible() && self.focused
7585 }
7586
7587 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7588 cx.notify();
7589 }
7590
7591 fn on_buffer_event(
7592 &mut self,
7593 multibuffer: ModelHandle<MultiBuffer>,
7594 event: &multi_buffer::Event,
7595 cx: &mut ViewContext<Self>,
7596 ) {
7597 match event {
7598 multi_buffer::Event::Edited => {
7599 self.refresh_active_diagnostics(cx);
7600 self.refresh_code_actions(cx);
7601 if self.has_active_copilot_suggestion(cx) {
7602 self.update_visible_copilot_suggestion(cx);
7603 }
7604 cx.emit(Event::BufferEdited);
7605
7606 if let Some(project) = &self.project {
7607 let project = project.read(cx);
7608 let languages_affected = multibuffer
7609 .read(cx)
7610 .all_buffers()
7611 .into_iter()
7612 .filter_map(|buffer| {
7613 let buffer = buffer.read(cx);
7614 let language = buffer.language()?;
7615 if project.is_local()
7616 && project.language_servers_for_buffer(buffer, cx).count() == 0
7617 {
7618 None
7619 } else {
7620 Some(language)
7621 }
7622 })
7623 .cloned()
7624 .collect::<HashSet<_>>();
7625 if !languages_affected.is_empty() {
7626 self.refresh_inlays(
7627 InlayRefreshReason::BufferEdited(languages_affected),
7628 cx,
7629 );
7630 }
7631 }
7632 }
7633 multi_buffer::Event::ExcerptsAdded {
7634 buffer,
7635 predecessor,
7636 excerpts,
7637 } => cx.emit(Event::ExcerptsAdded {
7638 buffer: buffer.clone(),
7639 predecessor: *predecessor,
7640 excerpts: excerpts.clone(),
7641 }),
7642 multi_buffer::Event::ExcerptsRemoved { ids } => {
7643 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7644 }
7645 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7646 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7647 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7648 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7649 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7650 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7651 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7652 multi_buffer::Event::DiagnosticsUpdated => {
7653 self.refresh_active_diagnostics(cx);
7654 }
7655 _ => {}
7656 };
7657 }
7658
7659 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7660 cx.notify();
7661 }
7662
7663 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7664 self.refresh_copilot_suggestions(true, cx);
7665 self.refresh_inlays(
7666 InlayRefreshReason::SettingsChange(inlay_hint_settings(
7667 self.selections.newest_anchor().head(),
7668 &self.buffer.read(cx).snapshot(cx),
7669 cx,
7670 )),
7671 cx,
7672 );
7673 }
7674
7675 pub fn set_searchable(&mut self, searchable: bool) {
7676 self.searchable = searchable;
7677 }
7678
7679 pub fn searchable(&self) -> bool {
7680 self.searchable
7681 }
7682
7683 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7684 let active_item = workspace.active_item(cx);
7685 let editor_handle = if let Some(editor) = active_item
7686 .as_ref()
7687 .and_then(|item| item.act_as::<Self>(cx))
7688 {
7689 editor
7690 } else {
7691 cx.propagate_action();
7692 return;
7693 };
7694
7695 let editor = editor_handle.read(cx);
7696 let buffer = editor.buffer.read(cx);
7697 if buffer.is_singleton() {
7698 cx.propagate_action();
7699 return;
7700 }
7701
7702 let mut new_selections_by_buffer = HashMap::default();
7703 for selection in editor.selections.all::<usize>(cx) {
7704 for (buffer, mut range, _) in
7705 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7706 {
7707 if selection.reversed {
7708 mem::swap(&mut range.start, &mut range.end);
7709 }
7710 new_selections_by_buffer
7711 .entry(buffer)
7712 .or_insert(Vec::new())
7713 .push(range)
7714 }
7715 }
7716
7717 editor_handle.update(cx, |editor, cx| {
7718 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7719 });
7720 let pane = workspace.active_pane().clone();
7721 pane.update(cx, |pane, _| pane.disable_history());
7722
7723 // We defer the pane interaction because we ourselves are a workspace item
7724 // and activating a new item causes the pane to call a method on us reentrantly,
7725 // which panics if we're on the stack.
7726 cx.defer(move |workspace, cx| {
7727 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7728 let editor = workspace.open_project_item::<Self>(buffer, cx);
7729 editor.update(cx, |editor, cx| {
7730 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7731 s.select_ranges(ranges);
7732 });
7733 });
7734 }
7735
7736 pane.update(cx, |pane, _| pane.enable_history());
7737 });
7738 }
7739
7740 fn jump(
7741 workspace: &mut Workspace,
7742 path: ProjectPath,
7743 position: Point,
7744 anchor: language::Anchor,
7745 cx: &mut ViewContext<Workspace>,
7746 ) {
7747 let editor = workspace.open_path(path, None, true, cx);
7748 cx.spawn(|_, mut cx| async move {
7749 let editor = editor
7750 .await?
7751 .downcast::<Editor>()
7752 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7753 .downgrade();
7754 editor.update(&mut cx, |editor, cx| {
7755 let buffer = editor
7756 .buffer()
7757 .read(cx)
7758 .as_singleton()
7759 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7760 let buffer = buffer.read(cx);
7761 let cursor = if buffer.can_resolve(&anchor) {
7762 language::ToPoint::to_point(&anchor, buffer)
7763 } else {
7764 buffer.clip_point(position, Bias::Left)
7765 };
7766
7767 let nav_history = editor.nav_history.take();
7768 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7769 s.select_ranges([cursor..cursor]);
7770 });
7771 editor.nav_history = nav_history;
7772
7773 anyhow::Ok(())
7774 })??;
7775
7776 anyhow::Ok(())
7777 })
7778 .detach_and_log_err(cx);
7779 }
7780
7781 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7782 let snapshot = self.buffer.read(cx).read(cx);
7783 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7784 Some(
7785 ranges
7786 .iter()
7787 .map(move |range| {
7788 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7789 })
7790 .collect(),
7791 )
7792 }
7793
7794 fn selection_replacement_ranges(
7795 &self,
7796 range: Range<OffsetUtf16>,
7797 cx: &AppContext,
7798 ) -> Vec<Range<OffsetUtf16>> {
7799 let selections = self.selections.all::<OffsetUtf16>(cx);
7800 let newest_selection = selections
7801 .iter()
7802 .max_by_key(|selection| selection.id)
7803 .unwrap();
7804 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7805 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7806 let snapshot = self.buffer.read(cx).read(cx);
7807 selections
7808 .into_iter()
7809 .map(|mut selection| {
7810 selection.start.0 =
7811 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7812 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7813 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7814 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7815 })
7816 .collect()
7817 }
7818
7819 fn report_copilot_event(
7820 &self,
7821 suggestion_id: Option<String>,
7822 suggestion_accepted: bool,
7823 cx: &AppContext,
7824 ) {
7825 let Some(project) = &self.project else {
7826 return
7827 };
7828
7829 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7830 let file_extension = self
7831 .buffer
7832 .read(cx)
7833 .as_singleton()
7834 .and_then(|b| b.read(cx).file())
7835 .and_then(|file| Path::new(file.file_name(cx)).extension())
7836 .and_then(|e| e.to_str())
7837 .map(|a| a.to_string());
7838
7839 let telemetry = project.read(cx).client().telemetry().clone();
7840 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7841
7842 let event = ClickhouseEvent::Copilot {
7843 suggestion_id,
7844 suggestion_accepted,
7845 file_extension,
7846 };
7847 telemetry.report_clickhouse_event(event, telemetry_settings);
7848 }
7849
7850 fn report_editor_event(
7851 &self,
7852 operation: &'static str,
7853 file_extension: Option<String>,
7854 cx: &AppContext,
7855 ) {
7856 let Some(project) = &self.project else {
7857 return
7858 };
7859
7860 // If None, we are in a file without an extension
7861 let file = self
7862 .buffer
7863 .read(cx)
7864 .as_singleton()
7865 .and_then(|b| b.read(cx).file());
7866 let file_extension = file_extension.or(file
7867 .as_ref()
7868 .and_then(|file| Path::new(file.file_name(cx)).extension())
7869 .and_then(|e| e.to_str())
7870 .map(|a| a.to_string()));
7871
7872 let vim_mode = cx
7873 .global::<SettingsStore>()
7874 .raw_user_settings()
7875 .get("vim_mode")
7876 == Some(&serde_json::Value::Bool(true));
7877 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7878 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7879 let copilot_enabled_for_language = self
7880 .buffer
7881 .read(cx)
7882 .settings_at(0, cx)
7883 .show_copilot_suggestions;
7884
7885 let telemetry = project.read(cx).client().telemetry().clone();
7886 let event = ClickhouseEvent::Editor {
7887 file_extension,
7888 vim_mode,
7889 operation,
7890 copilot_enabled,
7891 copilot_enabled_for_language,
7892 };
7893 telemetry.report_clickhouse_event(event, telemetry_settings)
7894 }
7895
7896 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7897 /// with each line being an array of {text, highlight} objects.
7898 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7899 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7900 return;
7901 };
7902
7903 #[derive(Serialize)]
7904 struct Chunk<'a> {
7905 text: String,
7906 highlight: Option<&'a str>,
7907 }
7908
7909 let snapshot = buffer.read(cx).snapshot();
7910 let range = self
7911 .selected_text_range(cx)
7912 .and_then(|selected_range| {
7913 if selected_range.is_empty() {
7914 None
7915 } else {
7916 Some(selected_range)
7917 }
7918 })
7919 .unwrap_or_else(|| 0..snapshot.len());
7920
7921 let chunks = snapshot.chunks(range, true);
7922 let mut lines = Vec::new();
7923 let mut line: VecDeque<Chunk> = VecDeque::new();
7924
7925 let theme = &theme::current(cx).editor.syntax;
7926
7927 for chunk in chunks {
7928 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7929 let mut chunk_lines = chunk.text.split("\n").peekable();
7930 while let Some(text) = chunk_lines.next() {
7931 let mut merged_with_last_token = false;
7932 if let Some(last_token) = line.back_mut() {
7933 if last_token.highlight == highlight {
7934 last_token.text.push_str(text);
7935 merged_with_last_token = true;
7936 }
7937 }
7938
7939 if !merged_with_last_token {
7940 line.push_back(Chunk {
7941 text: text.into(),
7942 highlight,
7943 });
7944 }
7945
7946 if chunk_lines.peek().is_some() {
7947 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7948 line.pop_front();
7949 }
7950 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7951 line.pop_back();
7952 }
7953
7954 lines.push(mem::take(&mut line));
7955 }
7956 }
7957 }
7958
7959 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7960 cx.write_to_clipboard(ClipboardItem::new(lines));
7961 }
7962
7963 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
7964 &self.inlay_hint_cache
7965 }
7966}
7967
7968fn inlay_hint_settings(
7969 location: Anchor,
7970 snapshot: &MultiBufferSnapshot,
7971 cx: &mut ViewContext<'_, '_, Editor>,
7972) -> InlayHintSettings {
7973 let file = snapshot.file_at(location);
7974 let language = snapshot.language_at(location);
7975 let settings = all_language_settings(file, cx);
7976 settings
7977 .language(language.map(|l| l.name()).as_deref())
7978 .inlay_hints
7979}
7980
7981fn consume_contiguous_rows(
7982 contiguous_row_selections: &mut Vec<Selection<Point>>,
7983 selection: &Selection<Point>,
7984 display_map: &DisplaySnapshot,
7985 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7986) -> (u32, u32) {
7987 contiguous_row_selections.push(selection.clone());
7988 let start_row = selection.start.row;
7989 let mut end_row = ending_row(selection, display_map);
7990
7991 while let Some(next_selection) = selections.peek() {
7992 if next_selection.start.row <= end_row {
7993 end_row = ending_row(next_selection, display_map);
7994 contiguous_row_selections.push(selections.next().unwrap().clone());
7995 } else {
7996 break;
7997 }
7998 }
7999 (start_row, end_row)
8000}
8001
8002fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
8003 if next_selection.end.column > 0 || next_selection.is_empty() {
8004 display_map.next_line_boundary(next_selection.end).0.row + 1
8005 } else {
8006 next_selection.end.row
8007 }
8008}
8009
8010impl EditorSnapshot {
8011 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
8012 self.display_snapshot.buffer_snapshot.language_at(position)
8013 }
8014
8015 pub fn is_focused(&self) -> bool {
8016 self.is_focused
8017 }
8018
8019 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
8020 self.placeholder_text.as_ref()
8021 }
8022
8023 pub fn scroll_position(&self) -> Vector2F {
8024 self.scroll_anchor.scroll_position(&self.display_snapshot)
8025 }
8026}
8027
8028impl Deref for EditorSnapshot {
8029 type Target = DisplaySnapshot;
8030
8031 fn deref(&self) -> &Self::Target {
8032 &self.display_snapshot
8033 }
8034}
8035
8036#[derive(Clone, Debug, PartialEq, Eq)]
8037pub enum Event {
8038 InputIgnored {
8039 text: Arc<str>,
8040 },
8041 ExcerptsAdded {
8042 buffer: ModelHandle<Buffer>,
8043 predecessor: ExcerptId,
8044 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
8045 },
8046 ExcerptsRemoved {
8047 ids: Vec<ExcerptId>,
8048 },
8049 BufferEdited,
8050 Edited,
8051 Reparsed,
8052 Focused,
8053 Blurred,
8054 DirtyChanged,
8055 Saved,
8056 TitleChanged,
8057 DiffBaseChanged,
8058 SelectionsChanged {
8059 local: bool,
8060 },
8061 ScrollPositionChanged {
8062 local: bool,
8063 autoscroll: bool,
8064 },
8065 Closed,
8066}
8067
8068pub struct EditorFocused(pub ViewHandle<Editor>);
8069pub struct EditorBlurred(pub ViewHandle<Editor>);
8070pub struct EditorReleased(pub WeakViewHandle<Editor>);
8071
8072impl Entity for Editor {
8073 type Event = Event;
8074
8075 fn release(&mut self, cx: &mut AppContext) {
8076 cx.emit_global(EditorReleased(self.handle.clone()));
8077 }
8078}
8079
8080impl View for Editor {
8081 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
8082 let style = self.style(cx);
8083 let font_changed = self.display_map.update(cx, |map, cx| {
8084 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
8085 map.set_font(style.text.font_id, style.text.font_size, cx)
8086 });
8087
8088 if font_changed {
8089 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
8090 hide_hover(editor, cx);
8091 hide_link_definition(editor, cx);
8092 });
8093 }
8094
8095 Stack::new()
8096 .with_child(EditorElement::new(style.clone()))
8097 .with_child(ChildView::new(&self.mouse_context_menu, cx))
8098 .into_any()
8099 }
8100
8101 fn ui_name() -> &'static str {
8102 "Editor"
8103 }
8104
8105 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8106 if cx.is_self_focused() {
8107 let focused_event = EditorFocused(cx.handle());
8108 cx.emit(Event::Focused);
8109 cx.emit_global(focused_event);
8110 }
8111 if let Some(rename) = self.pending_rename.as_ref() {
8112 cx.focus(&rename.editor);
8113 } else {
8114 if !self.focused {
8115 self.blink_manager.update(cx, BlinkManager::enable);
8116 }
8117 self.focused = true;
8118 self.buffer.update(cx, |buffer, cx| {
8119 buffer.finalize_last_transaction(cx);
8120 if self.leader_replica_id.is_none() {
8121 buffer.set_active_selections(
8122 &self.selections.disjoint_anchors(),
8123 self.selections.line_mode,
8124 self.cursor_shape,
8125 cx,
8126 );
8127 }
8128 });
8129 }
8130 }
8131
8132 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
8133 let blurred_event = EditorBlurred(cx.handle());
8134 cx.emit_global(blurred_event);
8135 self.focused = false;
8136 self.blink_manager.update(cx, BlinkManager::disable);
8137 self.buffer
8138 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
8139 self.hide_context_menu(cx);
8140 hide_hover(self, cx);
8141 cx.emit(Event::Blurred);
8142 cx.notify();
8143 }
8144
8145 fn modifiers_changed(
8146 &mut self,
8147 event: &gpui::platform::ModifiersChangedEvent,
8148 cx: &mut ViewContext<Self>,
8149 ) -> bool {
8150 let pending_selection = self.has_pending_selection();
8151
8152 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
8153 if event.cmd && !pending_selection {
8154 let snapshot = self.snapshot(cx);
8155 let kind = if event.shift {
8156 LinkDefinitionKind::Type
8157 } else {
8158 LinkDefinitionKind::Symbol
8159 };
8160
8161 show_link_definition(kind, self, point, snapshot, cx);
8162 return false;
8163 }
8164 }
8165
8166 {
8167 if self.link_go_to_definition_state.symbol_range.is_some()
8168 || !self.link_go_to_definition_state.definitions.is_empty()
8169 {
8170 self.link_go_to_definition_state.symbol_range.take();
8171 self.link_go_to_definition_state.definitions.clear();
8172 cx.notify();
8173 }
8174
8175 self.link_go_to_definition_state.task = None;
8176
8177 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8178 }
8179
8180 false
8181 }
8182
8183 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8184 Self::reset_to_default_keymap_context(keymap);
8185 let mode = match self.mode {
8186 EditorMode::SingleLine => "single_line",
8187 EditorMode::AutoHeight { .. } => "auto_height",
8188 EditorMode::Full => "full",
8189 };
8190 keymap.add_key("mode", mode);
8191 if self.pending_rename.is_some() {
8192 keymap.add_identifier("renaming");
8193 }
8194 match self.context_menu.as_ref() {
8195 Some(ContextMenu::Completions(_)) => {
8196 keymap.add_identifier("menu");
8197 keymap.add_identifier("showing_completions")
8198 }
8199 Some(ContextMenu::CodeActions(_)) => {
8200 keymap.add_identifier("menu");
8201 keymap.add_identifier("showing_code_actions")
8202 }
8203 None => {}
8204 }
8205 for layer in self.keymap_context_layers.values() {
8206 keymap.extend(layer);
8207 }
8208
8209 if let Some(extension) = self
8210 .buffer
8211 .read(cx)
8212 .as_singleton()
8213 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8214 {
8215 keymap.add_key("extension", extension.to_string());
8216 }
8217 }
8218
8219 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8220 Some(
8221 self.buffer
8222 .read(cx)
8223 .read(cx)
8224 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8225 .collect(),
8226 )
8227 }
8228
8229 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8230 // Prevent the IME menu from appearing when holding down an alphabetic key
8231 // while input is disabled.
8232 if !self.input_enabled {
8233 return None;
8234 }
8235
8236 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8237 Some(range.start.0..range.end.0)
8238 }
8239
8240 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8241 let snapshot = self.buffer.read(cx).read(cx);
8242 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8243 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8244 }
8245
8246 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8247 self.clear_text_highlights::<InputComposition>(cx);
8248 self.ime_transaction.take();
8249 }
8250
8251 fn replace_text_in_range(
8252 &mut self,
8253 range_utf16: Option<Range<usize>>,
8254 text: &str,
8255 cx: &mut ViewContext<Self>,
8256 ) {
8257 self.transact(cx, |this, cx| {
8258 if this.input_enabled {
8259 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8260 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8261 Some(this.selection_replacement_ranges(range_utf16, cx))
8262 } else {
8263 this.marked_text_ranges(cx)
8264 };
8265
8266 if let Some(new_selected_ranges) = new_selected_ranges {
8267 this.change_selections(None, cx, |selections| {
8268 selections.select_ranges(new_selected_ranges)
8269 });
8270 }
8271 }
8272
8273 this.handle_input(text, cx);
8274 });
8275
8276 if !self.input_enabled {
8277 return;
8278 }
8279
8280 if let Some(transaction) = self.ime_transaction {
8281 self.buffer.update(cx, |buffer, cx| {
8282 buffer.group_until_transaction(transaction, cx);
8283 });
8284 }
8285
8286 self.unmark_text(cx);
8287 }
8288
8289 fn replace_and_mark_text_in_range(
8290 &mut self,
8291 range_utf16: Option<Range<usize>>,
8292 text: &str,
8293 new_selected_range_utf16: Option<Range<usize>>,
8294 cx: &mut ViewContext<Self>,
8295 ) {
8296 if !self.input_enabled {
8297 return;
8298 }
8299
8300 let transaction = self.transact(cx, |this, cx| {
8301 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8302 let snapshot = this.buffer.read(cx).read(cx);
8303 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8304 for marked_range in &mut marked_ranges {
8305 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8306 marked_range.start.0 += relative_range_utf16.start;
8307 marked_range.start =
8308 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8309 marked_range.end =
8310 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8311 }
8312 }
8313 Some(marked_ranges)
8314 } else if let Some(range_utf16) = range_utf16 {
8315 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8316 Some(this.selection_replacement_ranges(range_utf16, cx))
8317 } else {
8318 None
8319 };
8320
8321 if let Some(ranges) = ranges_to_replace {
8322 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8323 }
8324
8325 let marked_ranges = {
8326 let snapshot = this.buffer.read(cx).read(cx);
8327 this.selections
8328 .disjoint_anchors()
8329 .iter()
8330 .map(|selection| {
8331 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8332 })
8333 .collect::<Vec<_>>()
8334 };
8335
8336 if text.is_empty() {
8337 this.unmark_text(cx);
8338 } else {
8339 this.highlight_text::<InputComposition>(
8340 marked_ranges.clone(),
8341 this.style(cx).composition_mark,
8342 cx,
8343 );
8344 }
8345
8346 this.handle_input(text, cx);
8347
8348 if let Some(new_selected_range) = new_selected_range_utf16 {
8349 let snapshot = this.buffer.read(cx).read(cx);
8350 let new_selected_ranges = marked_ranges
8351 .into_iter()
8352 .map(|marked_range| {
8353 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8354 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8355 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8356 snapshot.clip_offset_utf16(new_start, Bias::Left)
8357 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8358 })
8359 .collect::<Vec<_>>();
8360
8361 drop(snapshot);
8362 this.change_selections(None, cx, |selections| {
8363 selections.select_ranges(new_selected_ranges)
8364 });
8365 }
8366 });
8367
8368 self.ime_transaction = self.ime_transaction.or(transaction);
8369 if let Some(transaction) = self.ime_transaction {
8370 self.buffer.update(cx, |buffer, cx| {
8371 buffer.group_until_transaction(transaction, cx);
8372 });
8373 }
8374
8375 if self.text_highlights::<InputComposition>(cx).is_none() {
8376 self.ime_transaction.take();
8377 }
8378 }
8379}
8380
8381fn build_style(
8382 settings: &ThemeSettings,
8383 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8384 override_text_style: Option<&OverrideTextStyle>,
8385 cx: &AppContext,
8386) -> EditorStyle {
8387 let font_cache = cx.font_cache();
8388 let line_height_scalar = settings.line_height();
8389 let theme_id = settings.theme.meta.id;
8390 let mut theme = settings.theme.editor.clone();
8391 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8392 let field_editor_theme = get_field_editor_theme(&settings.theme);
8393 theme.text_color = field_editor_theme.text.color;
8394 theme.selection = field_editor_theme.selection;
8395 theme.background = field_editor_theme
8396 .container
8397 .background_color
8398 .unwrap_or_default();
8399 EditorStyle {
8400 text: field_editor_theme.text,
8401 placeholder_text: field_editor_theme.placeholder_text,
8402 line_height_scalar,
8403 theme,
8404 theme_id,
8405 }
8406 } else {
8407 let font_family_id = settings.buffer_font_family;
8408 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8409 let font_properties = Default::default();
8410 let font_id = font_cache
8411 .select_font(font_family_id, &font_properties)
8412 .unwrap();
8413 let font_size = settings.buffer_font_size(cx);
8414 EditorStyle {
8415 text: TextStyle {
8416 color: settings.theme.editor.text_color,
8417 font_family_name,
8418 font_family_id,
8419 font_id,
8420 font_size,
8421 font_properties,
8422 underline: Default::default(),
8423 },
8424 placeholder_text: None,
8425 line_height_scalar,
8426 theme,
8427 theme_id,
8428 }
8429 };
8430
8431 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8432 if let Some(highlighted) = style
8433 .text
8434 .clone()
8435 .highlight(highlight_style, font_cache)
8436 .log_err()
8437 {
8438 style.text = highlighted;
8439 }
8440 }
8441
8442 style
8443}
8444
8445trait SelectionExt {
8446 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8447 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8448 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8449 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8450 -> Range<u32>;
8451}
8452
8453impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8454 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8455 let start = self.start.to_point(buffer);
8456 let end = self.end.to_point(buffer);
8457 if self.reversed {
8458 end..start
8459 } else {
8460 start..end
8461 }
8462 }
8463
8464 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8465 let start = self.start.to_offset(buffer);
8466 let end = self.end.to_offset(buffer);
8467 if self.reversed {
8468 end..start
8469 } else {
8470 start..end
8471 }
8472 }
8473
8474 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8475 let start = self
8476 .start
8477 .to_point(&map.buffer_snapshot)
8478 .to_display_point(map);
8479 let end = self
8480 .end
8481 .to_point(&map.buffer_snapshot)
8482 .to_display_point(map);
8483 if self.reversed {
8484 end..start
8485 } else {
8486 start..end
8487 }
8488 }
8489
8490 fn spanned_rows(
8491 &self,
8492 include_end_if_at_line_start: bool,
8493 map: &DisplaySnapshot,
8494 ) -> Range<u32> {
8495 let start = self.start.to_point(&map.buffer_snapshot);
8496 let mut end = self.end.to_point(&map.buffer_snapshot);
8497 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8498 end.row -= 1;
8499 }
8500
8501 let buffer_start = map.prev_line_boundary(start).0;
8502 let buffer_end = map.next_line_boundary(end).0;
8503 buffer_start.row..buffer_end.row + 1
8504 }
8505}
8506
8507impl<T: InvalidationRegion> InvalidationStack<T> {
8508 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8509 where
8510 S: Clone + ToOffset,
8511 {
8512 while let Some(region) = self.last() {
8513 let all_selections_inside_invalidation_ranges =
8514 if selections.len() == region.ranges().len() {
8515 selections
8516 .iter()
8517 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8518 .all(|(selection, invalidation_range)| {
8519 let head = selection.head().to_offset(buffer);
8520 invalidation_range.start <= head && invalidation_range.end >= head
8521 })
8522 } else {
8523 false
8524 };
8525
8526 if all_selections_inside_invalidation_ranges {
8527 break;
8528 } else {
8529 self.pop();
8530 }
8531 }
8532 }
8533}
8534
8535impl<T> Default for InvalidationStack<T> {
8536 fn default() -> Self {
8537 Self(Default::default())
8538 }
8539}
8540
8541impl<T> Deref for InvalidationStack<T> {
8542 type Target = Vec<T>;
8543
8544 fn deref(&self) -> &Self::Target {
8545 &self.0
8546 }
8547}
8548
8549impl<T> DerefMut for InvalidationStack<T> {
8550 fn deref_mut(&mut self) -> &mut Self::Target {
8551 &mut self.0
8552 }
8553}
8554
8555impl InvalidationRegion for SnippetState {
8556 fn ranges(&self) -> &[Range<Anchor>] {
8557 &self.ranges[self.active_index]
8558 }
8559}
8560
8561impl Deref for EditorStyle {
8562 type Target = theme::Editor;
8563
8564 fn deref(&self) -> &Self::Target {
8565 &self.theme
8566 }
8567}
8568
8569pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8570 let mut highlighted_lines = Vec::new();
8571
8572 for (index, line) in diagnostic.message.lines().enumerate() {
8573 let line = match &diagnostic.source {
8574 Some(source) if index == 0 => {
8575 let source_highlight = Vec::from_iter(0..source.len());
8576 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8577 }
8578
8579 _ => highlight_diagnostic_message(Vec::new(), line),
8580 };
8581 highlighted_lines.push(line);
8582 }
8583 let message = diagnostic.message;
8584 Arc::new(move |cx: &mut BlockContext| {
8585 let message = message.clone();
8586 let settings = settings::get::<ThemeSettings>(cx);
8587 let tooltip_style = settings.theme.tooltip.clone();
8588 let theme = &settings.theme.editor;
8589 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8590 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8591 let anchor_x = cx.anchor_x;
8592 enum BlockContextToolip {}
8593 MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
8594 Flex::column()
8595 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8596 Label::new(
8597 line.clone(),
8598 style.message.clone().with_font_size(font_size),
8599 )
8600 .with_highlights(highlights.clone())
8601 .contained()
8602 .with_margin_left(anchor_x)
8603 }))
8604 .aligned()
8605 .left()
8606 .into_any()
8607 })
8608 .with_cursor_style(CursorStyle::PointingHand)
8609 .on_click(MouseButton::Left, move |_, _, cx| {
8610 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8611 })
8612 // We really need to rethink this ID system...
8613 .with_tooltip::<BlockContextToolip>(
8614 cx.block_id,
8615 "Copy diagnostic message".to_string(),
8616 None,
8617 tooltip_style,
8618 cx,
8619 )
8620 .into_any()
8621 })
8622}
8623
8624pub fn highlight_diagnostic_message(
8625 initial_highlights: Vec<usize>,
8626 message: &str,
8627) -> (String, Vec<usize>) {
8628 let mut message_without_backticks = String::new();
8629 let mut prev_offset = 0;
8630 let mut inside_block = false;
8631 let mut highlights = initial_highlights;
8632 for (match_ix, (offset, _)) in message
8633 .match_indices('`')
8634 .chain([(message.len(), "")])
8635 .enumerate()
8636 {
8637 message_without_backticks.push_str(&message[prev_offset..offset]);
8638 if inside_block {
8639 highlights.extend(prev_offset - match_ix..offset - match_ix);
8640 }
8641
8642 inside_block = !inside_block;
8643 prev_offset = offset + 1;
8644 }
8645
8646 (message_without_backticks, highlights)
8647}
8648
8649pub fn diagnostic_style(
8650 severity: DiagnosticSeverity,
8651 valid: bool,
8652 theme: &theme::Editor,
8653) -> DiagnosticStyle {
8654 match (severity, valid) {
8655 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8656 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8657 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8658 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8659 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8660 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8661 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8662 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8663 _ => theme.invalid_hint_diagnostic.clone(),
8664 }
8665}
8666
8667pub fn combine_syntax_and_fuzzy_match_highlights(
8668 text: &str,
8669 default_style: HighlightStyle,
8670 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8671 match_indices: &[usize],
8672) -> Vec<(Range<usize>, HighlightStyle)> {
8673 let mut result = Vec::new();
8674 let mut match_indices = match_indices.iter().copied().peekable();
8675
8676 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8677 {
8678 syntax_highlight.weight = None;
8679
8680 // Add highlights for any fuzzy match characters before the next
8681 // syntax highlight range.
8682 while let Some(&match_index) = match_indices.peek() {
8683 if match_index >= range.start {
8684 break;
8685 }
8686 match_indices.next();
8687 let end_index = char_ix_after(match_index, text);
8688 let mut match_style = default_style;
8689 match_style.weight = Some(fonts::Weight::BOLD);
8690 result.push((match_index..end_index, match_style));
8691 }
8692
8693 if range.start == usize::MAX {
8694 break;
8695 }
8696
8697 // Add highlights for any fuzzy match characters within the
8698 // syntax highlight range.
8699 let mut offset = range.start;
8700 while let Some(&match_index) = match_indices.peek() {
8701 if match_index >= range.end {
8702 break;
8703 }
8704
8705 match_indices.next();
8706 if match_index > offset {
8707 result.push((offset..match_index, syntax_highlight));
8708 }
8709
8710 let mut end_index = char_ix_after(match_index, text);
8711 while let Some(&next_match_index) = match_indices.peek() {
8712 if next_match_index == end_index && next_match_index < range.end {
8713 end_index = char_ix_after(next_match_index, text);
8714 match_indices.next();
8715 } else {
8716 break;
8717 }
8718 }
8719
8720 let mut match_style = syntax_highlight;
8721 match_style.weight = Some(fonts::Weight::BOLD);
8722 result.push((match_index..end_index, match_style));
8723 offset = end_index;
8724 }
8725
8726 if offset < range.end {
8727 result.push((offset..range.end, syntax_highlight));
8728 }
8729 }
8730
8731 fn char_ix_after(ix: usize, text: &str) -> usize {
8732 ix + text[ix..].chars().next().unwrap().len_utf8()
8733 }
8734
8735 result
8736}
8737
8738pub fn styled_runs_for_code_label<'a>(
8739 label: &'a CodeLabel,
8740 syntax_theme: &'a theme::SyntaxTheme,
8741) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8742 let fade_out = HighlightStyle {
8743 fade_out: Some(0.35),
8744 ..Default::default()
8745 };
8746
8747 let mut prev_end = label.filter_range.end;
8748 label
8749 .runs
8750 .iter()
8751 .enumerate()
8752 .flat_map(move |(ix, (range, highlight_id))| {
8753 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8754 style
8755 } else {
8756 return Default::default();
8757 };
8758 let mut muted_style = style;
8759 muted_style.highlight(fade_out);
8760
8761 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8762 if range.start >= label.filter_range.end {
8763 if range.start > prev_end {
8764 runs.push((prev_end..range.start, fade_out));
8765 }
8766 runs.push((range.clone(), muted_style));
8767 } else if range.end <= label.filter_range.end {
8768 runs.push((range.clone(), style));
8769 } else {
8770 runs.push((range.start..label.filter_range.end, style));
8771 runs.push((label.filter_range.end..range.end, muted_style));
8772 }
8773 prev_end = cmp::max(prev_end, range.end);
8774
8775 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8776 runs.push((prev_end..label.text.len(), fade_out));
8777 }
8778
8779 runs
8780 })
8781}
8782
8783pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8784 let mut index = 0;
8785 let mut codepoints = text.char_indices().peekable();
8786
8787 std::iter::from_fn(move || {
8788 let start_index = index;
8789 while let Some((new_index, codepoint)) = codepoints.next() {
8790 index = new_index + codepoint.len_utf8();
8791 let current_upper = codepoint.is_uppercase();
8792 let next_upper = codepoints
8793 .peek()
8794 .map(|(_, c)| c.is_uppercase())
8795 .unwrap_or(false);
8796
8797 if !current_upper && next_upper {
8798 return Some(&text[start_index..index]);
8799 }
8800 }
8801
8802 index = text.len();
8803 if start_index < text.len() {
8804 return Some(&text[start_index..]);
8805 }
8806 None
8807 })
8808 .flat_map(|word| word.split_inclusive('_'))
8809}
8810
8811trait RangeToAnchorExt {
8812 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8813}
8814
8815impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8816 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8817 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8818 }
8819}