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