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