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