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