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