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