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