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