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