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