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