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