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