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()),
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()),
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 (movement::start_of_paragraph(map, head), SelectionGoal::None)
5166 });
5167 })
5168 }
5169
5170 pub fn select_to_end_of_paragraph(
5171 &mut self,
5172 _: &SelectToEndOfParagraph,
5173 cx: &mut ViewContext<Self>,
5174 ) {
5175 if matches!(self.mode, EditorMode::SingleLine) {
5176 cx.propagate_action();
5177 return;
5178 }
5179
5180 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5181 s.move_heads_with(|map, head, _| {
5182 (movement::end_of_paragraph(map, head), SelectionGoal::None)
5183 });
5184 })
5185 }
5186
5187 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
5188 if matches!(self.mode, EditorMode::SingleLine) {
5189 cx.propagate_action();
5190 return;
5191 }
5192
5193 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5194 s.select_ranges(vec![0..0]);
5195 });
5196 }
5197
5198 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
5199 let mut selection = self.selections.last::<Point>(cx);
5200 selection.set_head(Point::zero(), SelectionGoal::None);
5201
5202 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5203 s.select(vec![selection]);
5204 });
5205 }
5206
5207 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
5208 if matches!(self.mode, EditorMode::SingleLine) {
5209 cx.propagate_action();
5210 return;
5211 }
5212
5213 let cursor = self.buffer.read(cx).read(cx).len();
5214 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5215 s.select_ranges(vec![cursor..cursor])
5216 });
5217 }
5218
5219 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
5220 self.nav_history = nav_history;
5221 }
5222
5223 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
5224 self.nav_history.as_ref()
5225 }
5226
5227 fn push_to_nav_history(
5228 &mut self,
5229 cursor_anchor: Anchor,
5230 new_position: Option<Point>,
5231 cx: &mut ViewContext<Self>,
5232 ) {
5233 if let Some(nav_history) = self.nav_history.as_mut() {
5234 let buffer = self.buffer.read(cx).read(cx);
5235 let cursor_position = cursor_anchor.to_point(&buffer);
5236 let scroll_state = self.scroll_manager.anchor();
5237 let scroll_top_row = scroll_state.top_row(&buffer);
5238 drop(buffer);
5239
5240 if let Some(new_position) = new_position {
5241 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
5242 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
5243 return;
5244 }
5245 }
5246
5247 nav_history.push(
5248 Some(NavigationData {
5249 cursor_anchor,
5250 cursor_position,
5251 scroll_anchor: scroll_state,
5252 scroll_top_row,
5253 }),
5254 cx,
5255 );
5256 }
5257 }
5258
5259 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
5260 let buffer = self.buffer.read(cx).snapshot(cx);
5261 let mut selection = self.selections.first::<usize>(cx);
5262 selection.set_head(buffer.len(), SelectionGoal::None);
5263 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5264 s.select(vec![selection]);
5265 });
5266 }
5267
5268 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
5269 let end = self.buffer.read(cx).read(cx).len();
5270 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5271 s.select_ranges(vec![0..end]);
5272 });
5273 }
5274
5275 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
5276 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5277 let mut selections = self.selections.all::<Point>(cx);
5278 let max_point = display_map.buffer_snapshot.max_point();
5279 for selection in &mut selections {
5280 let rows = selection.spanned_rows(true, &display_map);
5281 selection.start = Point::new(rows.start, 0);
5282 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
5283 selection.reversed = false;
5284 }
5285 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5286 s.select(selections);
5287 });
5288 }
5289
5290 pub fn split_selection_into_lines(
5291 &mut self,
5292 _: &SplitSelectionIntoLines,
5293 cx: &mut ViewContext<Self>,
5294 ) {
5295 let mut to_unfold = Vec::new();
5296 let mut new_selection_ranges = Vec::new();
5297 {
5298 let selections = self.selections.all::<Point>(cx);
5299 let buffer = self.buffer.read(cx).read(cx);
5300 for selection in selections {
5301 for row in selection.start.row..selection.end.row {
5302 let cursor = Point::new(row, buffer.line_len(row));
5303 new_selection_ranges.push(cursor..cursor);
5304 }
5305 new_selection_ranges.push(selection.end..selection.end);
5306 to_unfold.push(selection.start..selection.end);
5307 }
5308 }
5309 self.unfold_ranges(to_unfold, true, true, cx);
5310 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5311 s.select_ranges(new_selection_ranges);
5312 });
5313 }
5314
5315 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
5316 self.add_selection(true, cx);
5317 }
5318
5319 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
5320 self.add_selection(false, cx);
5321 }
5322
5323 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
5324 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5325 let mut selections = self.selections.all::<Point>(cx);
5326 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
5327 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
5328 let range = oldest_selection.display_range(&display_map).sorted();
5329 let columns = cmp::min(range.start.column(), range.end.column())
5330 ..cmp::max(range.start.column(), range.end.column());
5331
5332 selections.clear();
5333 let mut stack = Vec::new();
5334 for row in range.start.row()..=range.end.row() {
5335 if let Some(selection) = self.selections.build_columnar_selection(
5336 &display_map,
5337 row,
5338 &columns,
5339 oldest_selection.reversed,
5340 ) {
5341 stack.push(selection.id);
5342 selections.push(selection);
5343 }
5344 }
5345
5346 if above {
5347 stack.reverse();
5348 }
5349
5350 AddSelectionsState { above, stack }
5351 });
5352
5353 let last_added_selection = *state.stack.last().unwrap();
5354 let mut new_selections = Vec::new();
5355 if above == state.above {
5356 let end_row = if above {
5357 0
5358 } else {
5359 display_map.max_point().row()
5360 };
5361
5362 'outer: for selection in selections {
5363 if selection.id == last_added_selection {
5364 let range = selection.display_range(&display_map).sorted();
5365 debug_assert_eq!(range.start.row(), range.end.row());
5366 let mut row = range.start.row();
5367 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
5368 {
5369 start..end
5370 } else {
5371 cmp::min(range.start.column(), range.end.column())
5372 ..cmp::max(range.start.column(), range.end.column())
5373 };
5374
5375 while row != end_row {
5376 if above {
5377 row -= 1;
5378 } else {
5379 row += 1;
5380 }
5381
5382 if let Some(new_selection) = self.selections.build_columnar_selection(
5383 &display_map,
5384 row,
5385 &columns,
5386 selection.reversed,
5387 ) {
5388 state.stack.push(new_selection.id);
5389 if above {
5390 new_selections.push(new_selection);
5391 new_selections.push(selection);
5392 } else {
5393 new_selections.push(selection);
5394 new_selections.push(new_selection);
5395 }
5396
5397 continue 'outer;
5398 }
5399 }
5400 }
5401
5402 new_selections.push(selection);
5403 }
5404 } else {
5405 new_selections = selections;
5406 new_selections.retain(|s| s.id != last_added_selection);
5407 state.stack.pop();
5408 }
5409
5410 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5411 s.select(new_selections);
5412 });
5413 if state.stack.len() > 1 {
5414 self.add_selections_state = Some(state);
5415 }
5416 }
5417
5418 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
5419 self.push_to_selection_history();
5420 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5421 let buffer = &display_map.buffer_snapshot;
5422 let mut selections = self.selections.all::<usize>(cx);
5423 if let Some(mut select_next_state) = self.select_next_state.take() {
5424 let query = &select_next_state.query;
5425 if !select_next_state.done {
5426 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5427 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5428 let mut next_selected_range = None;
5429
5430 let bytes_after_last_selection =
5431 buffer.bytes_in_range(last_selection.end..buffer.len());
5432 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
5433 let query_matches = query
5434 .stream_find_iter(bytes_after_last_selection)
5435 .map(|result| (last_selection.end, result))
5436 .chain(
5437 query
5438 .stream_find_iter(bytes_before_first_selection)
5439 .map(|result| (0, result)),
5440 );
5441 for (start_offset, query_match) in query_matches {
5442 let query_match = query_match.unwrap(); // can only fail due to I/O
5443 let offset_range =
5444 start_offset + query_match.start()..start_offset + query_match.end();
5445 let display_range = offset_range.start.to_display_point(&display_map)
5446 ..offset_range.end.to_display_point(&display_map);
5447
5448 if !select_next_state.wordwise
5449 || (!movement::is_inside_word(&display_map, display_range.start)
5450 && !movement::is_inside_word(&display_map, display_range.end))
5451 {
5452 next_selected_range = Some(offset_range);
5453 break;
5454 }
5455 }
5456
5457 if let Some(next_selected_range) = next_selected_range {
5458 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5459 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5460 if action.replace_newest {
5461 s.delete(s.newest_anchor().id);
5462 }
5463 s.insert_range(next_selected_range);
5464 });
5465 } else {
5466 select_next_state.done = true;
5467 }
5468 }
5469
5470 self.select_next_state = Some(select_next_state);
5471 } else if selections.len() == 1 {
5472 let selection = selections.last_mut().unwrap();
5473 if selection.start == selection.end {
5474 let word_range = movement::surrounding_word(
5475 &display_map,
5476 selection.start.to_display_point(&display_map),
5477 );
5478 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5479 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5480 selection.goal = SelectionGoal::None;
5481 selection.reversed = false;
5482
5483 let query = buffer
5484 .text_for_range(selection.start..selection.end)
5485 .collect::<String>();
5486 let select_state = SelectNextState {
5487 query: AhoCorasick::new_auto_configured(&[query]),
5488 wordwise: true,
5489 done: false,
5490 };
5491 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5492 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5493 s.select(selections);
5494 });
5495 self.select_next_state = Some(select_state);
5496 } else {
5497 let query = buffer
5498 .text_for_range(selection.start..selection.end)
5499 .collect::<String>();
5500 self.select_next_state = Some(SelectNextState {
5501 query: AhoCorasick::new_auto_configured(&[query]),
5502 wordwise: false,
5503 done: false,
5504 });
5505 self.select_next(action, cx);
5506 }
5507 }
5508 }
5509
5510 pub fn select_previous(&mut self, action: &SelectPrevious, cx: &mut ViewContext<Self>) {
5511 self.push_to_selection_history();
5512 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5513 let buffer = &display_map.buffer_snapshot;
5514 let mut selections = self.selections.all::<usize>(cx);
5515 if let Some(mut select_prev_state) = self.select_prev_state.take() {
5516 let query = &select_prev_state.query;
5517 if !select_prev_state.done {
5518 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
5519 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
5520 let mut next_selected_range = None;
5521 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
5522 let bytes_before_last_selection =
5523 buffer.reversed_bytes_in_range(0..last_selection.start);
5524 let bytes_after_first_selection =
5525 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
5526 let query_matches = query
5527 .stream_find_iter(bytes_before_last_selection)
5528 .map(|result| (last_selection.start, result))
5529 .chain(
5530 query
5531 .stream_find_iter(bytes_after_first_selection)
5532 .map(|result| (buffer.len(), result)),
5533 );
5534 for (end_offset, query_match) in query_matches {
5535 let query_match = query_match.unwrap(); // can only fail due to I/O
5536 let offset_range =
5537 end_offset - query_match.end()..end_offset - query_match.start();
5538 let display_range = offset_range.start.to_display_point(&display_map)
5539 ..offset_range.end.to_display_point(&display_map);
5540
5541 if !select_prev_state.wordwise
5542 || (!movement::is_inside_word(&display_map, display_range.start)
5543 && !movement::is_inside_word(&display_map, display_range.end))
5544 {
5545 next_selected_range = Some(offset_range);
5546 break;
5547 }
5548 }
5549
5550 if let Some(next_selected_range) = next_selected_range {
5551 self.unfold_ranges([next_selected_range.clone()], false, true, cx);
5552 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5553 if action.replace_newest {
5554 s.delete(s.newest_anchor().id);
5555 }
5556 s.insert_range(next_selected_range);
5557 });
5558 } else {
5559 select_prev_state.done = true;
5560 }
5561 }
5562
5563 self.select_prev_state = Some(select_prev_state);
5564 } else if selections.len() == 1 {
5565 let selection = selections.last_mut().unwrap();
5566 if selection.start == selection.end {
5567 let word_range = movement::surrounding_word(
5568 &display_map,
5569 selection.start.to_display_point(&display_map),
5570 );
5571 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
5572 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
5573 selection.goal = SelectionGoal::None;
5574 selection.reversed = false;
5575
5576 let query = buffer
5577 .text_for_range(selection.start..selection.end)
5578 .collect::<String>();
5579 let query = query.chars().rev().collect::<String>();
5580 let select_state = SelectNextState {
5581 query: AhoCorasick::new_auto_configured(&[query]),
5582 wordwise: true,
5583 done: false,
5584 };
5585 self.unfold_ranges([selection.start..selection.end], false, true, cx);
5586 self.change_selections(Some(Autoscroll::newest()), cx, |s| {
5587 s.select(selections);
5588 });
5589 self.select_prev_state = Some(select_state);
5590 } else {
5591 let query = buffer
5592 .text_for_range(selection.start..selection.end)
5593 .collect::<String>();
5594 let query = query.chars().rev().collect::<String>();
5595 self.select_prev_state = Some(SelectNextState {
5596 query: AhoCorasick::new_auto_configured(&[query]),
5597 wordwise: false,
5598 done: false,
5599 });
5600 self.select_previous(action, cx);
5601 }
5602 }
5603 }
5604
5605 pub fn toggle_comments(&mut self, action: &ToggleComments, cx: &mut ViewContext<Self>) {
5606 self.transact(cx, |this, cx| {
5607 let mut selections = this.selections.all::<Point>(cx);
5608 let mut edits = Vec::new();
5609 let mut selection_edit_ranges = Vec::new();
5610 let mut last_toggled_row = None;
5611 let snapshot = this.buffer.read(cx).read(cx);
5612 let empty_str: Arc<str> = "".into();
5613 let mut suffixes_inserted = Vec::new();
5614
5615 fn comment_prefix_range(
5616 snapshot: &MultiBufferSnapshot,
5617 row: u32,
5618 comment_prefix: &str,
5619 comment_prefix_whitespace: &str,
5620 ) -> Range<Point> {
5621 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
5622
5623 let mut line_bytes = snapshot
5624 .bytes_in_range(start..snapshot.max_point())
5625 .flatten()
5626 .copied();
5627
5628 // If this line currently begins with the line comment prefix, then record
5629 // the range containing the prefix.
5630 if line_bytes
5631 .by_ref()
5632 .take(comment_prefix.len())
5633 .eq(comment_prefix.bytes())
5634 {
5635 // Include any whitespace that matches the comment prefix.
5636 let matching_whitespace_len = line_bytes
5637 .zip(comment_prefix_whitespace.bytes())
5638 .take_while(|(a, b)| a == b)
5639 .count() as u32;
5640 let end = Point::new(
5641 start.row,
5642 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
5643 );
5644 start..end
5645 } else {
5646 start..start
5647 }
5648 }
5649
5650 fn comment_suffix_range(
5651 snapshot: &MultiBufferSnapshot,
5652 row: u32,
5653 comment_suffix: &str,
5654 comment_suffix_has_leading_space: bool,
5655 ) -> Range<Point> {
5656 let end = Point::new(row, snapshot.line_len(row));
5657 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
5658
5659 let mut line_end_bytes = snapshot
5660 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
5661 .flatten()
5662 .copied();
5663
5664 let leading_space_len = if suffix_start_column > 0
5665 && line_end_bytes.next() == Some(b' ')
5666 && comment_suffix_has_leading_space
5667 {
5668 1
5669 } else {
5670 0
5671 };
5672
5673 // If this line currently begins with the line comment prefix, then record
5674 // the range containing the prefix.
5675 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
5676 let start = Point::new(end.row, suffix_start_column - leading_space_len);
5677 start..end
5678 } else {
5679 end..end
5680 }
5681 }
5682
5683 // TODO: Handle selections that cross excerpts
5684 for selection in &mut selections {
5685 let start_column = snapshot.indent_size_for_line(selection.start.row).len;
5686 let language = if let Some(language) =
5687 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
5688 {
5689 language
5690 } else {
5691 continue;
5692 };
5693
5694 selection_edit_ranges.clear();
5695
5696 // If multiple selections contain a given row, avoid processing that
5697 // row more than once.
5698 let mut start_row = selection.start.row;
5699 if last_toggled_row == Some(start_row) {
5700 start_row += 1;
5701 }
5702 let end_row =
5703 if selection.end.row > selection.start.row && selection.end.column == 0 {
5704 selection.end.row - 1
5705 } else {
5706 selection.end.row
5707 };
5708 last_toggled_row = Some(end_row);
5709
5710 if start_row > end_row {
5711 continue;
5712 }
5713
5714 // If the language has line comments, toggle those.
5715 if let Some(full_comment_prefix) = language.line_comment_prefix() {
5716 // Split the comment prefix's trailing whitespace into a separate string,
5717 // as that portion won't be used for detecting if a line is a comment.
5718 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5719 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5720 let mut all_selection_lines_are_comments = true;
5721
5722 for row in start_row..=end_row {
5723 if snapshot.is_line_blank(row) && start_row < end_row {
5724 continue;
5725 }
5726
5727 let prefix_range = comment_prefix_range(
5728 snapshot.deref(),
5729 row,
5730 comment_prefix,
5731 comment_prefix_whitespace,
5732 );
5733 if prefix_range.is_empty() {
5734 all_selection_lines_are_comments = false;
5735 }
5736 selection_edit_ranges.push(prefix_range);
5737 }
5738
5739 if all_selection_lines_are_comments {
5740 edits.extend(
5741 selection_edit_ranges
5742 .iter()
5743 .cloned()
5744 .map(|range| (range, empty_str.clone())),
5745 );
5746 } else {
5747 let min_column = selection_edit_ranges
5748 .iter()
5749 .map(|r| r.start.column)
5750 .min()
5751 .unwrap_or(0);
5752 edits.extend(selection_edit_ranges.iter().map(|range| {
5753 let position = Point::new(range.start.row, min_column);
5754 (position..position, full_comment_prefix.clone())
5755 }));
5756 }
5757 } else if let Some((full_comment_prefix, comment_suffix)) =
5758 language.block_comment_delimiters()
5759 {
5760 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
5761 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
5762 let prefix_range = comment_prefix_range(
5763 snapshot.deref(),
5764 start_row,
5765 comment_prefix,
5766 comment_prefix_whitespace,
5767 );
5768 let suffix_range = comment_suffix_range(
5769 snapshot.deref(),
5770 end_row,
5771 comment_suffix.trim_start_matches(' '),
5772 comment_suffix.starts_with(' '),
5773 );
5774
5775 if prefix_range.is_empty() || suffix_range.is_empty() {
5776 edits.push((
5777 prefix_range.start..prefix_range.start,
5778 full_comment_prefix.clone(),
5779 ));
5780 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
5781 suffixes_inserted.push((end_row, comment_suffix.len()));
5782 } else {
5783 edits.push((prefix_range, empty_str.clone()));
5784 edits.push((suffix_range, empty_str.clone()));
5785 }
5786 } else {
5787 continue;
5788 }
5789 }
5790
5791 drop(snapshot);
5792 this.buffer.update(cx, |buffer, cx| {
5793 buffer.edit(edits, None, cx);
5794 });
5795
5796 // Adjust selections so that they end before any comment suffixes that
5797 // were inserted.
5798 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
5799 let mut selections = this.selections.all::<Point>(cx);
5800 let snapshot = this.buffer.read(cx).read(cx);
5801 for selection in &mut selections {
5802 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
5803 match row.cmp(&selection.end.row) {
5804 Ordering::Less => {
5805 suffixes_inserted.next();
5806 continue;
5807 }
5808 Ordering::Greater => break,
5809 Ordering::Equal => {
5810 if selection.end.column == snapshot.line_len(row) {
5811 if selection.is_empty() {
5812 selection.start.column -= suffix_len as u32;
5813 }
5814 selection.end.column -= suffix_len as u32;
5815 }
5816 break;
5817 }
5818 }
5819 }
5820 }
5821
5822 drop(snapshot);
5823 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
5824
5825 let selections = this.selections.all::<Point>(cx);
5826 let selections_on_single_row = selections.windows(2).all(|selections| {
5827 selections[0].start.row == selections[1].start.row
5828 && selections[0].end.row == selections[1].end.row
5829 && selections[0].start.row == selections[0].end.row
5830 });
5831 let selections_selecting = selections
5832 .iter()
5833 .any(|selection| selection.start != selection.end);
5834 let advance_downwards = action.advance_downwards
5835 && selections_on_single_row
5836 && !selections_selecting
5837 && this.mode != EditorMode::SingleLine;
5838
5839 if advance_downwards {
5840 let snapshot = this.buffer.read(cx).snapshot(cx);
5841
5842 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5843 s.move_cursors_with(|display_snapshot, display_point, _| {
5844 let mut point = display_point.to_point(display_snapshot);
5845 point.row += 1;
5846 point = snapshot.clip_point(point, Bias::Left);
5847 let display_point = point.to_display_point(display_snapshot);
5848 (display_point, SelectionGoal::Column(display_point.column()))
5849 })
5850 });
5851 }
5852 });
5853 }
5854
5855 pub fn select_larger_syntax_node(
5856 &mut self,
5857 _: &SelectLargerSyntaxNode,
5858 cx: &mut ViewContext<Self>,
5859 ) {
5860 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5861 let buffer = self.buffer.read(cx).snapshot(cx);
5862 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
5863
5864 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5865 let mut selected_larger_node = false;
5866 let new_selections = old_selections
5867 .iter()
5868 .map(|selection| {
5869 let old_range = selection.start..selection.end;
5870 let mut new_range = old_range.clone();
5871 while let Some(containing_range) =
5872 buffer.range_for_syntax_ancestor(new_range.clone())
5873 {
5874 new_range = containing_range;
5875 if !display_map.intersects_fold(new_range.start)
5876 && !display_map.intersects_fold(new_range.end)
5877 {
5878 break;
5879 }
5880 }
5881
5882 selected_larger_node |= new_range != old_range;
5883 Selection {
5884 id: selection.id,
5885 start: new_range.start,
5886 end: new_range.end,
5887 goal: SelectionGoal::None,
5888 reversed: selection.reversed,
5889 }
5890 })
5891 .collect::<Vec<_>>();
5892
5893 if selected_larger_node {
5894 stack.push(old_selections);
5895 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5896 s.select(new_selections);
5897 });
5898 }
5899 self.select_larger_syntax_node_stack = stack;
5900 }
5901
5902 pub fn select_smaller_syntax_node(
5903 &mut self,
5904 _: &SelectSmallerSyntaxNode,
5905 cx: &mut ViewContext<Self>,
5906 ) {
5907 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
5908 if let Some(selections) = stack.pop() {
5909 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5910 s.select(selections.to_vec());
5911 });
5912 }
5913 self.select_larger_syntax_node_stack = stack;
5914 }
5915
5916 pub fn move_to_enclosing_bracket(
5917 &mut self,
5918 _: &MoveToEnclosingBracket,
5919 cx: &mut ViewContext<Self>,
5920 ) {
5921 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
5922 s.move_offsets_with(|snapshot, selection| {
5923 let Some(enclosing_bracket_ranges) = snapshot.enclosing_bracket_ranges(selection.start..selection.end) else {
5924 return;
5925 };
5926
5927 let mut best_length = usize::MAX;
5928 let mut best_inside = false;
5929 let mut best_in_bracket_range = false;
5930 let mut best_destination = None;
5931 for (open, close) in enclosing_bracket_ranges {
5932 let close = close.to_inclusive();
5933 let length = close.end() - open.start;
5934 let inside = selection.start >= open.end && selection.end <= *close.start();
5935 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
5936
5937 // If best is next to a bracket and current isn't, skip
5938 if !in_bracket_range && best_in_bracket_range {
5939 continue;
5940 }
5941
5942 // Prefer smaller lengths unless best is inside and current isn't
5943 if length > best_length && (best_inside || !inside) {
5944 continue;
5945 }
5946
5947 best_length = length;
5948 best_inside = inside;
5949 best_in_bracket_range = in_bracket_range;
5950 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
5951 if inside {
5952 open.end
5953 } else {
5954 open.start
5955 }
5956 } else {
5957 if inside {
5958 *close.start()
5959 } else {
5960 *close.end()
5961 }
5962 });
5963 }
5964
5965 if let Some(destination) = best_destination {
5966 selection.collapse_to(destination, SelectionGoal::None);
5967 }
5968 })
5969 });
5970 }
5971
5972 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
5973 self.end_selection(cx);
5974 self.selection_history.mode = SelectionHistoryMode::Undoing;
5975 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
5976 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5977 self.select_next_state = entry.select_next_state;
5978 self.select_prev_state = entry.select_prev_state;
5979 self.add_selections_state = entry.add_selections_state;
5980 self.request_autoscroll(Autoscroll::newest(), cx);
5981 }
5982 self.selection_history.mode = SelectionHistoryMode::Normal;
5983 }
5984
5985 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
5986 self.end_selection(cx);
5987 self.selection_history.mode = SelectionHistoryMode::Redoing;
5988 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
5989 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
5990 self.select_next_state = entry.select_next_state;
5991 self.select_prev_state = entry.select_prev_state;
5992 self.add_selections_state = entry.add_selections_state;
5993 self.request_autoscroll(Autoscroll::newest(), cx);
5994 }
5995 self.selection_history.mode = SelectionHistoryMode::Normal;
5996 }
5997
5998 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
5999 self.go_to_diagnostic_impl(Direction::Next, cx)
6000 }
6001
6002 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
6003 self.go_to_diagnostic_impl(Direction::Prev, cx)
6004 }
6005
6006 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
6007 let buffer = self.buffer.read(cx).snapshot(cx);
6008 let selection = self.selections.newest::<usize>(cx);
6009
6010 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
6011 if direction == Direction::Next {
6012 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
6013 let (group_id, jump_to) = popover.activation_info();
6014 if self.activate_diagnostics(group_id, cx) {
6015 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6016 let mut new_selection = s.newest_anchor().clone();
6017 new_selection.collapse_to(jump_to, SelectionGoal::None);
6018 s.select_anchors(vec![new_selection.clone()]);
6019 });
6020 }
6021 return;
6022 }
6023 }
6024
6025 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
6026 active_diagnostics
6027 .primary_range
6028 .to_offset(&buffer)
6029 .to_inclusive()
6030 });
6031 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
6032 if active_primary_range.contains(&selection.head()) {
6033 *active_primary_range.end()
6034 } else {
6035 selection.head()
6036 }
6037 } else {
6038 selection.head()
6039 };
6040
6041 loop {
6042 let mut diagnostics = if direction == Direction::Prev {
6043 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
6044 } else {
6045 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
6046 };
6047 let group = diagnostics.find_map(|entry| {
6048 if entry.diagnostic.is_primary
6049 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
6050 && !entry.range.is_empty()
6051 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
6052 {
6053 Some((entry.range, entry.diagnostic.group_id))
6054 } else {
6055 None
6056 }
6057 });
6058
6059 if let Some((primary_range, group_id)) = group {
6060 if self.activate_diagnostics(group_id, cx) {
6061 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6062 s.select(vec![Selection {
6063 id: selection.id,
6064 start: primary_range.start,
6065 end: primary_range.start,
6066 reversed: false,
6067 goal: SelectionGoal::None,
6068 }]);
6069 });
6070 }
6071 break;
6072 } else {
6073 // Cycle around to the start of the buffer, potentially moving back to the start of
6074 // the currently active diagnostic.
6075 active_primary_range.take();
6076 if direction == Direction::Prev {
6077 if search_start == buffer.len() {
6078 break;
6079 } else {
6080 search_start = buffer.len();
6081 }
6082 } else if search_start == 0 {
6083 break;
6084 } else {
6085 search_start = 0;
6086 }
6087 }
6088 }
6089 }
6090
6091 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
6092 let snapshot = self
6093 .display_map
6094 .update(cx, |display_map, cx| display_map.snapshot(cx));
6095 let selection = self.selections.newest::<Point>(cx);
6096
6097 if !self.seek_in_direction(
6098 &snapshot,
6099 selection.head(),
6100 false,
6101 snapshot
6102 .buffer_snapshot
6103 .git_diff_hunks_in_range((selection.head().row + 1)..u32::MAX),
6104 cx,
6105 ) {
6106 let wrapped_point = Point::zero();
6107 self.seek_in_direction(
6108 &snapshot,
6109 wrapped_point,
6110 true,
6111 snapshot
6112 .buffer_snapshot
6113 .git_diff_hunks_in_range((wrapped_point.row + 1)..u32::MAX),
6114 cx,
6115 );
6116 }
6117 }
6118
6119 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
6120 let snapshot = self
6121 .display_map
6122 .update(cx, |display_map, cx| display_map.snapshot(cx));
6123 let selection = self.selections.newest::<Point>(cx);
6124
6125 if !self.seek_in_direction(
6126 &snapshot,
6127 selection.head(),
6128 false,
6129 snapshot
6130 .buffer_snapshot
6131 .git_diff_hunks_in_range_rev(0..selection.head().row),
6132 cx,
6133 ) {
6134 let wrapped_point = snapshot.buffer_snapshot.max_point();
6135 self.seek_in_direction(
6136 &snapshot,
6137 wrapped_point,
6138 true,
6139 snapshot
6140 .buffer_snapshot
6141 .git_diff_hunks_in_range_rev(0..wrapped_point.row),
6142 cx,
6143 );
6144 }
6145 }
6146
6147 fn seek_in_direction(
6148 &mut self,
6149 snapshot: &DisplaySnapshot,
6150 initial_point: Point,
6151 is_wrapped: bool,
6152 hunks: impl Iterator<Item = DiffHunk<u32>>,
6153 cx: &mut ViewContext<Editor>,
6154 ) -> bool {
6155 let display_point = initial_point.to_display_point(snapshot);
6156 let mut hunks = hunks
6157 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
6158 .skip_while(|hunk| {
6159 if is_wrapped {
6160 false
6161 } else {
6162 hunk.contains_display_row(display_point.row())
6163 }
6164 })
6165 .dedup();
6166
6167 if let Some(hunk) = hunks.next() {
6168 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6169 let row = hunk.start_display_row();
6170 let point = DisplayPoint::new(row, 0);
6171 s.select_display_ranges([point..point]);
6172 });
6173
6174 true
6175 } else {
6176 false
6177 }
6178 }
6179
6180 pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext<Self>) {
6181 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx);
6182 }
6183
6184 pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext<Self>) {
6185 self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx);
6186 }
6187
6188 fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext<Self>) {
6189 let Some(workspace) = self.workspace(cx) else { return };
6190 let buffer = self.buffer.read(cx);
6191 let head = self.selections.newest::<usize>(cx).head();
6192 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
6193 text_anchor
6194 } else {
6195 return;
6196 };
6197
6198 let project = workspace.read(cx).project().clone();
6199 let definitions = project.update(cx, |project, cx| match kind {
6200 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
6201 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
6202 });
6203
6204 cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move {
6205 let definitions = definitions.await?;
6206 editor.update(&mut cx, |editor, cx| {
6207 editor.navigate_to_definitions(definitions, cx);
6208 })?;
6209 Ok::<(), anyhow::Error>(())
6210 })
6211 .detach_and_log_err(cx);
6212 }
6213
6214 pub fn navigate_to_definitions(
6215 &mut self,
6216 mut definitions: Vec<LocationLink>,
6217 cx: &mut ViewContext<Editor>,
6218 ) {
6219 let Some(workspace) = self.workspace(cx) else { return };
6220 let pane = workspace.read(cx).active_pane().clone();
6221 // If there is one definition, just open it directly
6222 if definitions.len() == 1 {
6223 let definition = definitions.pop().unwrap();
6224 let range = definition
6225 .target
6226 .range
6227 .to_offset(definition.target.buffer.read(cx));
6228
6229 if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
6230 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
6231 s.select_ranges([range]);
6232 });
6233 } else {
6234 cx.window_context().defer(move |cx| {
6235 let target_editor: ViewHandle<Self> = workspace.update(cx, |workspace, cx| {
6236 workspace.open_project_item(definition.target.buffer.clone(), cx)
6237 });
6238 target_editor.update(cx, |target_editor, cx| {
6239 // When selecting a definition in a different buffer, disable the nav history
6240 // to avoid creating a history entry at the previous cursor location.
6241 pane.update(cx, |pane, _| pane.disable_history());
6242 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
6243 s.select_ranges([range]);
6244 });
6245 pane.update(cx, |pane, _| pane.enable_history());
6246 });
6247 });
6248 }
6249 } else if !definitions.is_empty() {
6250 let replica_id = self.replica_id(cx);
6251 cx.window_context().defer(move |cx| {
6252 let title = definitions
6253 .iter()
6254 .find(|definition| definition.origin.is_some())
6255 .and_then(|definition| {
6256 definition.origin.as_ref().map(|origin| {
6257 let buffer = origin.buffer.read(cx);
6258 format!(
6259 "Definitions for {}",
6260 buffer
6261 .text_for_range(origin.range.clone())
6262 .collect::<String>()
6263 )
6264 })
6265 })
6266 .unwrap_or("Definitions".to_owned());
6267 let locations = definitions
6268 .into_iter()
6269 .map(|definition| definition.target)
6270 .collect();
6271 workspace.update(cx, |workspace, cx| {
6272 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
6273 });
6274 });
6275 }
6276 }
6277
6278 pub fn find_all_references(
6279 workspace: &mut Workspace,
6280 _: &FindAllReferences,
6281 cx: &mut ViewContext<Workspace>,
6282 ) -> Option<Task<Result<()>>> {
6283 let active_item = workspace.active_item(cx)?;
6284 let editor_handle = active_item.act_as::<Self>(cx)?;
6285
6286 let editor = editor_handle.read(cx);
6287 let buffer = editor.buffer.read(cx);
6288 let head = editor.selections.newest::<usize>(cx).head();
6289 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
6290 let replica_id = editor.replica_id(cx);
6291
6292 let project = workspace.project().clone();
6293 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
6294 Some(cx.spawn_labeled(
6295 "Finding All References...",
6296 |workspace, mut cx| async move {
6297 let locations = references.await?;
6298 if locations.is_empty() {
6299 return Ok(());
6300 }
6301
6302 workspace.update(&mut cx, |workspace, cx| {
6303 let title = locations
6304 .first()
6305 .as_ref()
6306 .map(|location| {
6307 let buffer = location.buffer.read(cx);
6308 format!(
6309 "References to `{}`",
6310 buffer
6311 .text_for_range(location.range.clone())
6312 .collect::<String>()
6313 )
6314 })
6315 .unwrap();
6316 Self::open_locations_in_multibuffer(
6317 workspace, locations, replica_id, title, cx,
6318 );
6319 })?;
6320
6321 Ok(())
6322 },
6323 ))
6324 }
6325
6326 /// Opens a multibuffer with the given project locations in it
6327 pub fn open_locations_in_multibuffer(
6328 workspace: &mut Workspace,
6329 mut locations: Vec<Location>,
6330 replica_id: ReplicaId,
6331 title: String,
6332 cx: &mut ViewContext<Workspace>,
6333 ) {
6334 // If there are multiple definitions, open them in a multibuffer
6335 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
6336 let mut locations = locations.into_iter().peekable();
6337 let mut ranges_to_highlight = Vec::new();
6338
6339 let excerpt_buffer = cx.add_model(|cx| {
6340 let mut multibuffer = MultiBuffer::new(replica_id);
6341 while let Some(location) = locations.next() {
6342 let buffer = location.buffer.read(cx);
6343 let mut ranges_for_buffer = Vec::new();
6344 let range = location.range.to_offset(buffer);
6345 ranges_for_buffer.push(range.clone());
6346
6347 while let Some(next_location) = locations.peek() {
6348 if next_location.buffer == location.buffer {
6349 ranges_for_buffer.push(next_location.range.to_offset(buffer));
6350 locations.next();
6351 } else {
6352 break;
6353 }
6354 }
6355
6356 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
6357 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
6358 location.buffer.clone(),
6359 ranges_for_buffer,
6360 1,
6361 cx,
6362 ))
6363 }
6364
6365 multibuffer.with_title(title)
6366 });
6367
6368 let editor = cx.add_view(|cx| {
6369 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
6370 });
6371 editor.update(cx, |editor, cx| {
6372 editor.highlight_background::<Self>(
6373 ranges_to_highlight,
6374 |theme| theme.editor.highlighted_line_background,
6375 cx,
6376 );
6377 });
6378 workspace.add_item(Box::new(editor), cx);
6379 }
6380
6381 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6382 use language::ToOffset as _;
6383
6384 let project = self.project.clone()?;
6385 let selection = self.selections.newest_anchor().clone();
6386 let (cursor_buffer, cursor_buffer_position) = self
6387 .buffer
6388 .read(cx)
6389 .text_anchor_for_position(selection.head(), cx)?;
6390 let (tail_buffer, _) = self
6391 .buffer
6392 .read(cx)
6393 .text_anchor_for_position(selection.tail(), cx)?;
6394 if tail_buffer != cursor_buffer {
6395 return None;
6396 }
6397
6398 let snapshot = cursor_buffer.read(cx).snapshot();
6399 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
6400 let prepare_rename = project.update(cx, |project, cx| {
6401 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
6402 });
6403
6404 Some(cx.spawn(|this, mut cx| async move {
6405 let rename_range = if let Some(range) = prepare_rename.await? {
6406 Some(range)
6407 } else {
6408 this.read_with(&cx, |this, cx| {
6409 let buffer = this.buffer.read(cx).snapshot(cx);
6410 let mut buffer_highlights = this
6411 .document_highlights_for_position(selection.head(), &buffer)
6412 .filter(|highlight| {
6413 highlight.start.excerpt_id() == selection.head().excerpt_id()
6414 && highlight.end.excerpt_id() == selection.head().excerpt_id()
6415 });
6416 buffer_highlights
6417 .next()
6418 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
6419 })?
6420 };
6421 if let Some(rename_range) = rename_range {
6422 let rename_buffer_range = rename_range.to_offset(&snapshot);
6423 let cursor_offset_in_rename_range =
6424 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
6425
6426 this.update(&mut cx, |this, cx| {
6427 this.take_rename(false, cx);
6428 let style = this.style(cx);
6429 let buffer = this.buffer.read(cx).read(cx);
6430 let cursor_offset = selection.head().to_offset(&buffer);
6431 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
6432 let rename_end = rename_start + rename_buffer_range.len();
6433 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
6434 let mut old_highlight_id = None;
6435 let old_name: Arc<str> = buffer
6436 .chunks(rename_start..rename_end, true)
6437 .map(|chunk| {
6438 if old_highlight_id.is_none() {
6439 old_highlight_id = chunk.syntax_highlight_id;
6440 }
6441 chunk.text
6442 })
6443 .collect::<String>()
6444 .into();
6445
6446 drop(buffer);
6447
6448 // Position the selection in the rename editor so that it matches the current selection.
6449 this.show_local_selections = false;
6450 let rename_editor = cx.add_view(|cx| {
6451 let mut editor = Editor::single_line(None, cx);
6452 if let Some(old_highlight_id) = old_highlight_id {
6453 editor.override_text_style =
6454 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
6455 }
6456 editor.buffer.update(cx, |buffer, cx| {
6457 buffer.edit([(0..0, old_name.clone())], None, cx)
6458 });
6459 editor.select_all(&SelectAll, cx);
6460 editor
6461 });
6462
6463 let ranges = this
6464 .clear_background_highlights::<DocumentHighlightWrite>(cx)
6465 .into_iter()
6466 .flat_map(|(_, ranges)| ranges)
6467 .chain(
6468 this.clear_background_highlights::<DocumentHighlightRead>(cx)
6469 .into_iter()
6470 .flat_map(|(_, ranges)| ranges),
6471 )
6472 .collect();
6473
6474 this.highlight_text::<Rename>(
6475 ranges,
6476 HighlightStyle {
6477 fade_out: Some(style.rename_fade),
6478 ..Default::default()
6479 },
6480 cx,
6481 );
6482 cx.focus(&rename_editor);
6483 let block_id = this.insert_blocks(
6484 [BlockProperties {
6485 style: BlockStyle::Flex,
6486 position: range.start.clone(),
6487 height: 1,
6488 render: Arc::new({
6489 let editor = rename_editor.clone();
6490 move |cx: &mut BlockContext| {
6491 ChildView::new(&editor, cx)
6492 .contained()
6493 .with_padding_left(cx.anchor_x)
6494 .into_any()
6495 }
6496 }),
6497 disposition: BlockDisposition::Below,
6498 }],
6499 Some(Autoscroll::fit()),
6500 cx,
6501 )[0];
6502 this.pending_rename = Some(RenameState {
6503 range,
6504 old_name,
6505 editor: rename_editor,
6506 block_id,
6507 });
6508 })?;
6509 }
6510
6511 Ok(())
6512 }))
6513 }
6514
6515 pub fn confirm_rename(
6516 workspace: &mut Workspace,
6517 _: &ConfirmRename,
6518 cx: &mut ViewContext<Workspace>,
6519 ) -> Option<Task<Result<()>>> {
6520 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
6521
6522 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
6523 let rename = editor.take_rename(false, cx)?;
6524 let buffer = editor.buffer.read(cx);
6525 let (start_buffer, start) =
6526 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
6527 let (end_buffer, end) =
6528 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
6529 if start_buffer == end_buffer {
6530 let new_name = rename.editor.read(cx).text(cx);
6531 Some((start_buffer, start..end, rename.old_name, new_name))
6532 } else {
6533 None
6534 }
6535 })?;
6536
6537 let rename = workspace.project().clone().update(cx, |project, cx| {
6538 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
6539 });
6540
6541 let editor = editor.downgrade();
6542 Some(cx.spawn(|workspace, mut cx| async move {
6543 let project_transaction = rename.await?;
6544 Self::open_project_transaction(
6545 &editor,
6546 workspace,
6547 project_transaction,
6548 format!("Rename: {} → {}", old_name, new_name),
6549 cx.clone(),
6550 )
6551 .await?;
6552
6553 editor.update(&mut cx, |editor, cx| {
6554 editor.refresh_document_highlights(cx);
6555 })?;
6556 Ok(())
6557 }))
6558 }
6559
6560 fn take_rename(
6561 &mut self,
6562 moving_cursor: bool,
6563 cx: &mut ViewContext<Self>,
6564 ) -> Option<RenameState> {
6565 let rename = self.pending_rename.take()?;
6566 self.remove_blocks(
6567 [rename.block_id].into_iter().collect(),
6568 Some(Autoscroll::fit()),
6569 cx,
6570 );
6571 self.clear_text_highlights::<Rename>(cx);
6572 self.show_local_selections = true;
6573
6574 if moving_cursor {
6575 let rename_editor = rename.editor.read(cx);
6576 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
6577
6578 // Update the selection to match the position of the selection inside
6579 // the rename editor.
6580 let snapshot = self.buffer.read(cx).read(cx);
6581 let rename_range = rename.range.to_offset(&snapshot);
6582 let cursor_in_editor = snapshot
6583 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
6584 .min(rename_range.end);
6585 drop(snapshot);
6586
6587 self.change_selections(None, cx, |s| {
6588 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
6589 });
6590 } else {
6591 self.refresh_document_highlights(cx);
6592 }
6593
6594 Some(rename)
6595 }
6596
6597 #[cfg(any(test, feature = "test-support"))]
6598 pub fn pending_rename(&self) -> Option<&RenameState> {
6599 self.pending_rename.as_ref()
6600 }
6601
6602 fn format(&mut self, _: &Format, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
6603 let project = match &self.project {
6604 Some(project) => project.clone(),
6605 None => return None,
6606 };
6607
6608 Some(self.perform_format(project, FormatTrigger::Manual, cx))
6609 }
6610
6611 fn perform_format(
6612 &mut self,
6613 project: ModelHandle<Project>,
6614 trigger: FormatTrigger,
6615 cx: &mut ViewContext<Self>,
6616 ) -> Task<Result<()>> {
6617 let buffer = self.buffer().clone();
6618 let buffers = buffer.read(cx).all_buffers();
6619
6620 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
6621 let format = project.update(cx, |project, cx| project.format(buffers, true, trigger, cx));
6622
6623 cx.spawn(|_, mut cx| async move {
6624 let transaction = futures::select_biased! {
6625 _ = timeout => {
6626 log::warn!("timed out waiting for formatting");
6627 None
6628 }
6629 transaction = format.log_err().fuse() => transaction,
6630 };
6631
6632 buffer.update(&mut cx, |buffer, cx| {
6633 if let Some(transaction) = transaction {
6634 if !buffer.is_singleton() {
6635 buffer.push_transaction(&transaction.0, cx);
6636 }
6637 }
6638
6639 cx.notify();
6640 });
6641
6642 Ok(())
6643 })
6644 }
6645
6646 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
6647 if let Some(project) = self.project.clone() {
6648 self.buffer.update(cx, |multi_buffer, cx| {
6649 project.update(cx, |project, cx| {
6650 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
6651 });
6652 })
6653 }
6654 }
6655
6656 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
6657 cx.show_character_palette();
6658 }
6659
6660 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
6661 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
6662 let buffer = self.buffer.read(cx).snapshot(cx);
6663 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
6664 let is_valid = buffer
6665 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
6666 .any(|entry| {
6667 entry.diagnostic.is_primary
6668 && !entry.range.is_empty()
6669 && entry.range.start == primary_range_start
6670 && entry.diagnostic.message == active_diagnostics.primary_message
6671 });
6672
6673 if is_valid != active_diagnostics.is_valid {
6674 active_diagnostics.is_valid = is_valid;
6675 let mut new_styles = HashMap::default();
6676 for (block_id, diagnostic) in &active_diagnostics.blocks {
6677 new_styles.insert(
6678 *block_id,
6679 diagnostic_block_renderer(diagnostic.clone(), is_valid),
6680 );
6681 }
6682 self.display_map
6683 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
6684 }
6685 }
6686 }
6687
6688 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
6689 self.dismiss_diagnostics(cx);
6690 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
6691 let buffer = self.buffer.read(cx).snapshot(cx);
6692
6693 let mut primary_range = None;
6694 let mut primary_message = None;
6695 let mut group_end = Point::zero();
6696 let diagnostic_group = buffer
6697 .diagnostic_group::<Point>(group_id)
6698 .map(|entry| {
6699 if entry.range.end > group_end {
6700 group_end = entry.range.end;
6701 }
6702 if entry.diagnostic.is_primary {
6703 primary_range = Some(entry.range.clone());
6704 primary_message = Some(entry.diagnostic.message.clone());
6705 }
6706 entry
6707 })
6708 .collect::<Vec<_>>();
6709 let primary_range = primary_range?;
6710 let primary_message = primary_message?;
6711 let primary_range =
6712 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
6713
6714 let blocks = display_map
6715 .insert_blocks(
6716 diagnostic_group.iter().map(|entry| {
6717 let diagnostic = entry.diagnostic.clone();
6718 let message_height = diagnostic.message.lines().count() as u8;
6719 BlockProperties {
6720 style: BlockStyle::Fixed,
6721 position: buffer.anchor_after(entry.range.start),
6722 height: message_height,
6723 render: diagnostic_block_renderer(diagnostic, true),
6724 disposition: BlockDisposition::Below,
6725 }
6726 }),
6727 cx,
6728 )
6729 .into_iter()
6730 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
6731 .collect();
6732
6733 Some(ActiveDiagnosticGroup {
6734 primary_range,
6735 primary_message,
6736 blocks,
6737 is_valid: true,
6738 })
6739 });
6740 self.active_diagnostics.is_some()
6741 }
6742
6743 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
6744 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
6745 self.display_map.update(cx, |display_map, cx| {
6746 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
6747 });
6748 cx.notify();
6749 }
6750 }
6751
6752 pub fn set_selections_from_remote(
6753 &mut self,
6754 selections: Vec<Selection<Anchor>>,
6755 pending_selection: Option<Selection<Anchor>>,
6756 cx: &mut ViewContext<Self>,
6757 ) {
6758 let old_cursor_position = self.selections.newest_anchor().head();
6759 self.selections.change_with(cx, |s| {
6760 s.select_anchors(selections);
6761 if let Some(pending_selection) = pending_selection {
6762 s.set_pending(pending_selection, SelectMode::Character);
6763 } else {
6764 s.clear_pending();
6765 }
6766 });
6767 self.selections_did_change(false, &old_cursor_position, cx);
6768 }
6769
6770 fn push_to_selection_history(&mut self) {
6771 self.selection_history.push(SelectionHistoryEntry {
6772 selections: self.selections.disjoint_anchors(),
6773 select_next_state: self.select_next_state.clone(),
6774 select_prev_state: self.select_prev_state.clone(),
6775 add_selections_state: self.add_selections_state.clone(),
6776 });
6777 }
6778
6779 pub fn transact(
6780 &mut self,
6781 cx: &mut ViewContext<Self>,
6782 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
6783 ) -> Option<TransactionId> {
6784 self.start_transaction_at(Instant::now(), cx);
6785 update(self, cx);
6786 self.end_transaction_at(Instant::now(), cx)
6787 }
6788
6789 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
6790 self.end_selection(cx);
6791 if let Some(tx_id) = self
6792 .buffer
6793 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
6794 {
6795 self.selection_history
6796 .insert_transaction(tx_id, self.selections.disjoint_anchors());
6797 }
6798 }
6799
6800 fn end_transaction_at(
6801 &mut self,
6802 now: Instant,
6803 cx: &mut ViewContext<Self>,
6804 ) -> Option<TransactionId> {
6805 if let Some(tx_id) = self
6806 .buffer
6807 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
6808 {
6809 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
6810 *end_selections = Some(self.selections.disjoint_anchors());
6811 } else {
6812 error!("unexpectedly ended a transaction that wasn't started by this editor");
6813 }
6814
6815 cx.emit(Event::Edited);
6816 Some(tx_id)
6817 } else {
6818 None
6819 }
6820 }
6821
6822 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
6823 let mut fold_ranges = Vec::new();
6824
6825 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6826
6827 let selections = self.selections.all::<Point>(cx);
6828 for selection in selections {
6829 let range = selection.range().sorted();
6830 let buffer_start_row = range.start.row;
6831
6832 for row in (0..=range.end.row).rev() {
6833 let fold_range = display_map.foldable_range(row);
6834
6835 if let Some(fold_range) = fold_range {
6836 if fold_range.end.row >= buffer_start_row {
6837 fold_ranges.push(fold_range);
6838 if row <= range.start.row {
6839 break;
6840 }
6841 }
6842 }
6843 }
6844 }
6845
6846 self.fold_ranges(fold_ranges, true, cx);
6847 }
6848
6849 pub fn fold_at(&mut self, fold_at: &FoldAt, cx: &mut ViewContext<Self>) {
6850 let buffer_row = fold_at.buffer_row;
6851 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6852
6853 if let Some(fold_range) = display_map.foldable_range(buffer_row) {
6854 let autoscroll = self
6855 .selections
6856 .all::<Point>(cx)
6857 .iter()
6858 .any(|selection| fold_range.overlaps(&selection.range()));
6859
6860 self.fold_ranges(std::iter::once(fold_range), autoscroll, cx);
6861 }
6862 }
6863
6864 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
6865 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6866 let buffer = &display_map.buffer_snapshot;
6867 let selections = self.selections.all::<Point>(cx);
6868 let ranges = selections
6869 .iter()
6870 .map(|s| {
6871 let range = s.display_range(&display_map).sorted();
6872 let mut start = range.start.to_point(&display_map);
6873 let mut end = range.end.to_point(&display_map);
6874 start.column = 0;
6875 end.column = buffer.line_len(end.row);
6876 start..end
6877 })
6878 .collect::<Vec<_>>();
6879
6880 self.unfold_ranges(ranges, true, true, cx);
6881 }
6882
6883 pub fn unfold_at(&mut self, unfold_at: &UnfoldAt, cx: &mut ViewContext<Self>) {
6884 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
6885
6886 let intersection_range = Point::new(unfold_at.buffer_row, 0)
6887 ..Point::new(
6888 unfold_at.buffer_row,
6889 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
6890 );
6891
6892 let autoscroll = self
6893 .selections
6894 .all::<Point>(cx)
6895 .iter()
6896 .any(|selection| selection.range().overlaps(&intersection_range));
6897
6898 self.unfold_ranges(std::iter::once(intersection_range), true, autoscroll, cx)
6899 }
6900
6901 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
6902 let selections = self.selections.all::<Point>(cx);
6903 let ranges = selections.into_iter().map(|s| s.start..s.end);
6904 self.fold_ranges(ranges, true, cx);
6905 }
6906
6907 pub fn fold_ranges<T: ToOffset + Clone>(
6908 &mut self,
6909 ranges: impl IntoIterator<Item = Range<T>>,
6910 auto_scroll: bool,
6911 cx: &mut ViewContext<Self>,
6912 ) {
6913 let mut ranges = ranges.into_iter().peekable();
6914 if ranges.peek().is_some() {
6915 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
6916
6917 if auto_scroll {
6918 self.request_autoscroll(Autoscroll::fit(), cx);
6919 }
6920
6921 cx.notify();
6922 }
6923 }
6924
6925 pub fn unfold_ranges<T: ToOffset + Clone>(
6926 &mut self,
6927 ranges: impl IntoIterator<Item = Range<T>>,
6928 inclusive: bool,
6929 auto_scroll: bool,
6930 cx: &mut ViewContext<Self>,
6931 ) {
6932 let mut ranges = ranges.into_iter().peekable();
6933 if ranges.peek().is_some() {
6934 self.display_map
6935 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
6936 if auto_scroll {
6937 self.request_autoscroll(Autoscroll::fit(), cx);
6938 }
6939
6940 cx.notify();
6941 }
6942 }
6943
6944 pub fn gutter_hover(
6945 &mut self,
6946 GutterHover { hovered }: &GutterHover,
6947 cx: &mut ViewContext<Self>,
6948 ) {
6949 self.gutter_hovered = *hovered;
6950 cx.notify();
6951 }
6952
6953 pub fn insert_blocks(
6954 &mut self,
6955 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
6956 autoscroll: Option<Autoscroll>,
6957 cx: &mut ViewContext<Self>,
6958 ) -> Vec<BlockId> {
6959 let blocks = self
6960 .display_map
6961 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
6962 if let Some(autoscroll) = autoscroll {
6963 self.request_autoscroll(autoscroll, cx);
6964 }
6965 blocks
6966 }
6967
6968 pub fn replace_blocks(
6969 &mut self,
6970 blocks: HashMap<BlockId, RenderBlock>,
6971 autoscroll: Option<Autoscroll>,
6972 cx: &mut ViewContext<Self>,
6973 ) {
6974 self.display_map
6975 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
6976 if let Some(autoscroll) = autoscroll {
6977 self.request_autoscroll(autoscroll, cx);
6978 }
6979 }
6980
6981 pub fn remove_blocks(
6982 &mut self,
6983 block_ids: HashSet<BlockId>,
6984 autoscroll: Option<Autoscroll>,
6985 cx: &mut ViewContext<Self>,
6986 ) {
6987 self.display_map.update(cx, |display_map, cx| {
6988 display_map.remove_blocks(block_ids, cx)
6989 });
6990 if let Some(autoscroll) = autoscroll {
6991 self.request_autoscroll(autoscroll, cx);
6992 }
6993 }
6994
6995 pub fn longest_row(&self, cx: &mut AppContext) -> u32 {
6996 self.display_map
6997 .update(cx, |map, cx| map.snapshot(cx))
6998 .longest_row()
6999 }
7000
7001 pub fn max_point(&self, cx: &mut AppContext) -> DisplayPoint {
7002 self.display_map
7003 .update(cx, |map, cx| map.snapshot(cx))
7004 .max_point()
7005 }
7006
7007 pub fn text(&self, cx: &AppContext) -> String {
7008 self.buffer.read(cx).read(cx).text()
7009 }
7010
7011 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
7012 self.transact(cx, |this, cx| {
7013 this.buffer
7014 .read(cx)
7015 .as_singleton()
7016 .expect("you can only call set_text on editors for singleton buffers")
7017 .update(cx, |buffer, cx| buffer.set_text(text, cx));
7018 });
7019 }
7020
7021 pub fn display_text(&self, cx: &mut AppContext) -> String {
7022 self.display_map
7023 .update(cx, |map, cx| map.snapshot(cx))
7024 .text()
7025 }
7026
7027 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
7028 let settings = self.buffer.read(cx).settings_at(0, cx);
7029 let mode = self
7030 .soft_wrap_mode_override
7031 .unwrap_or_else(|| settings.soft_wrap);
7032 match mode {
7033 language_settings::SoftWrap::None => SoftWrap::None,
7034 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
7035 language_settings::SoftWrap::PreferredLineLength => {
7036 SoftWrap::Column(settings.preferred_line_length)
7037 }
7038 }
7039 }
7040
7041 pub fn set_soft_wrap_mode(
7042 &mut self,
7043 mode: language_settings::SoftWrap,
7044 cx: &mut ViewContext<Self>,
7045 ) {
7046 self.soft_wrap_mode_override = Some(mode);
7047 cx.notify();
7048 }
7049
7050 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut AppContext) -> bool {
7051 self.display_map
7052 .update(cx, |map, cx| map.set_wrap_width(width, cx))
7053 }
7054
7055 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
7056 if self.soft_wrap_mode_override.is_some() {
7057 self.soft_wrap_mode_override.take();
7058 } else {
7059 let soft_wrap = match self.soft_wrap_mode(cx) {
7060 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
7061 SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
7062 };
7063 self.soft_wrap_mode_override = Some(soft_wrap);
7064 }
7065 cx.notify();
7066 }
7067
7068 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
7069 self.show_gutter = show_gutter;
7070 cx.notify();
7071 }
7072
7073 pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
7074 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7075 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7076 cx.reveal_path(&file.abs_path(cx));
7077 }
7078 }
7079 }
7080
7081 pub fn copy_path(&mut self, _: &CopyPath, cx: &mut ViewContext<Self>) {
7082 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7083 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7084 if let Some(path) = file.abs_path(cx).to_str() {
7085 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7086 }
7087 }
7088 }
7089 }
7090
7091 pub fn copy_relative_path(&mut self, _: &CopyRelativePath, cx: &mut ViewContext<Self>) {
7092 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
7093 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
7094 if let Some(path) = file.path().to_str() {
7095 cx.write_to_clipboard(ClipboardItem::new(path.to_string()));
7096 }
7097 }
7098 }
7099 }
7100
7101 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
7102 self.highlighted_rows = rows;
7103 }
7104
7105 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
7106 self.highlighted_rows.clone()
7107 }
7108
7109 pub fn highlight_background<T: 'static>(
7110 &mut self,
7111 ranges: Vec<Range<Anchor>>,
7112 color_fetcher: fn(&Theme) -> Color,
7113 cx: &mut ViewContext<Self>,
7114 ) {
7115 self.background_highlights
7116 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
7117 cx.notify();
7118 }
7119
7120 #[allow(clippy::type_complexity)]
7121 pub fn clear_background_highlights<T: 'static>(
7122 &mut self,
7123 cx: &mut ViewContext<Self>,
7124 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
7125 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
7126 if highlights.is_some() {
7127 cx.notify();
7128 }
7129 highlights
7130 }
7131
7132 #[cfg(feature = "test-support")]
7133 pub fn all_background_highlights(
7134 &mut self,
7135 cx: &mut ViewContext<Self>,
7136 ) -> Vec<(Range<DisplayPoint>, Color)> {
7137 let snapshot = self.snapshot(cx);
7138 let buffer = &snapshot.buffer_snapshot;
7139 let start = buffer.anchor_before(0);
7140 let end = buffer.anchor_after(buffer.len());
7141 let theme = theme::current(cx);
7142 self.background_highlights_in_range(start..end, &snapshot, theme.as_ref())
7143 }
7144
7145 fn document_highlights_for_position<'a>(
7146 &'a self,
7147 position: Anchor,
7148 buffer: &'a MultiBufferSnapshot,
7149 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
7150 let read_highlights = self
7151 .background_highlights
7152 .get(&TypeId::of::<DocumentHighlightRead>())
7153 .map(|h| &h.1);
7154 let write_highlights = self
7155 .background_highlights
7156 .get(&TypeId::of::<DocumentHighlightWrite>())
7157 .map(|h| &h.1);
7158 let left_position = position.bias_left(buffer);
7159 let right_position = position.bias_right(buffer);
7160 read_highlights
7161 .into_iter()
7162 .chain(write_highlights)
7163 .flat_map(move |ranges| {
7164 let start_ix = match ranges.binary_search_by(|probe| {
7165 let cmp = probe.end.cmp(&left_position, buffer);
7166 if cmp.is_ge() {
7167 Ordering::Greater
7168 } else {
7169 Ordering::Less
7170 }
7171 }) {
7172 Ok(i) | Err(i) => i,
7173 };
7174
7175 let right_position = right_position.clone();
7176 ranges[start_ix..]
7177 .iter()
7178 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
7179 })
7180 }
7181
7182 pub fn background_highlights_in_range(
7183 &self,
7184 search_range: Range<Anchor>,
7185 display_snapshot: &DisplaySnapshot,
7186 theme: &Theme,
7187 ) -> Vec<(Range<DisplayPoint>, Color)> {
7188 let mut results = Vec::new();
7189 let buffer = &display_snapshot.buffer_snapshot;
7190 for (color_fetcher, ranges) in self.background_highlights.values() {
7191 let color = color_fetcher(theme);
7192 let start_ix = match ranges.binary_search_by(|probe| {
7193 let cmp = probe.end.cmp(&search_range.start, buffer);
7194 if cmp.is_gt() {
7195 Ordering::Greater
7196 } else {
7197 Ordering::Less
7198 }
7199 }) {
7200 Ok(i) | Err(i) => i,
7201 };
7202 for range in &ranges[start_ix..] {
7203 if range.start.cmp(&search_range.end, buffer).is_ge() {
7204 break;
7205 }
7206 let start = range
7207 .start
7208 .to_point(buffer)
7209 .to_display_point(display_snapshot);
7210 let end = range
7211 .end
7212 .to_point(buffer)
7213 .to_display_point(display_snapshot);
7214 results.push((start..end, color))
7215 }
7216 }
7217 results
7218 }
7219
7220 pub fn highlight_text<T: 'static>(
7221 &mut self,
7222 ranges: Vec<Range<Anchor>>,
7223 style: HighlightStyle,
7224 cx: &mut ViewContext<Self>,
7225 ) {
7226 self.display_map.update(cx, |map, _| {
7227 map.highlight_text(TypeId::of::<T>(), ranges, style)
7228 });
7229 cx.notify();
7230 }
7231
7232 pub fn text_highlights<'a, T: 'static>(
7233 &'a self,
7234 cx: &'a AppContext,
7235 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
7236 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
7237 }
7238
7239 pub fn clear_text_highlights<T: 'static>(
7240 &mut self,
7241 cx: &mut ViewContext<Self>,
7242 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
7243 let highlights = self
7244 .display_map
7245 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
7246 if highlights.is_some() {
7247 cx.notify();
7248 }
7249 highlights
7250 }
7251
7252 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
7253 self.blink_manager.read(cx).visible() && self.focused
7254 }
7255
7256 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
7257 cx.notify();
7258 }
7259
7260 fn on_buffer_event(
7261 &mut self,
7262 multibuffer: ModelHandle<MultiBuffer>,
7263 event: &multi_buffer::Event,
7264 cx: &mut ViewContext<Self>,
7265 ) {
7266 match event {
7267 multi_buffer::Event::Edited => {
7268 self.refresh_active_diagnostics(cx);
7269 self.refresh_code_actions(cx);
7270 if self.has_active_copilot_suggestion(cx) {
7271 self.update_visible_copilot_suggestion(cx);
7272 }
7273 cx.emit(Event::BufferEdited);
7274
7275 if let Some(project) = &self.project {
7276 let project = project.read(cx);
7277 let languages_affected = multibuffer
7278 .read(cx)
7279 .all_buffers()
7280 .into_iter()
7281 .filter_map(|buffer| {
7282 let buffer = buffer.read(cx);
7283 let language = buffer.language()?;
7284 if project.is_local()
7285 && project.language_servers_for_buffer(buffer, cx).count() == 0
7286 {
7287 None
7288 } else {
7289 Some(language)
7290 }
7291 })
7292 .cloned()
7293 .collect::<HashSet<_>>();
7294 if !languages_affected.is_empty() {
7295 self.refresh_inlays(
7296 InlayRefreshReason::BufferEdited(languages_affected),
7297 cx,
7298 );
7299 }
7300 }
7301 }
7302 multi_buffer::Event::ExcerptsAdded {
7303 buffer,
7304 predecessor,
7305 excerpts,
7306 } => cx.emit(Event::ExcerptsAdded {
7307 buffer: buffer.clone(),
7308 predecessor: *predecessor,
7309 excerpts: excerpts.clone(),
7310 }),
7311 multi_buffer::Event::ExcerptsRemoved { ids } => {
7312 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
7313 }
7314 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
7315 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
7316 multi_buffer::Event::Saved => cx.emit(Event::Saved),
7317 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
7318 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
7319 multi_buffer::Event::DiffBaseChanged => cx.emit(Event::DiffBaseChanged),
7320 multi_buffer::Event::Closed => cx.emit(Event::Closed),
7321 multi_buffer::Event::DiagnosticsUpdated => {
7322 self.refresh_active_diagnostics(cx);
7323 }
7324 _ => {}
7325 };
7326 }
7327
7328 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
7329 cx.notify();
7330 }
7331
7332 fn settings_changed(&mut self, cx: &mut ViewContext<Self>) {
7333 self.refresh_copilot_suggestions(true, cx);
7334 self.refresh_inlays(
7335 InlayRefreshReason::SettingsChange(inlay_hint_settings(
7336 self.selections.newest_anchor().head(),
7337 &self.buffer.read(cx).snapshot(cx),
7338 cx,
7339 )),
7340 cx,
7341 );
7342 }
7343
7344 pub fn set_searchable(&mut self, searchable: bool) {
7345 self.searchable = searchable;
7346 }
7347
7348 pub fn searchable(&self) -> bool {
7349 self.searchable
7350 }
7351
7352 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
7353 let active_item = workspace.active_item(cx);
7354 let editor_handle = if let Some(editor) = active_item
7355 .as_ref()
7356 .and_then(|item| item.act_as::<Self>(cx))
7357 {
7358 editor
7359 } else {
7360 cx.propagate_action();
7361 return;
7362 };
7363
7364 let editor = editor_handle.read(cx);
7365 let buffer = editor.buffer.read(cx);
7366 if buffer.is_singleton() {
7367 cx.propagate_action();
7368 return;
7369 }
7370
7371 let mut new_selections_by_buffer = HashMap::default();
7372 for selection in editor.selections.all::<usize>(cx) {
7373 for (buffer, mut range, _) in
7374 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
7375 {
7376 if selection.reversed {
7377 mem::swap(&mut range.start, &mut range.end);
7378 }
7379 new_selections_by_buffer
7380 .entry(buffer)
7381 .or_insert(Vec::new())
7382 .push(range)
7383 }
7384 }
7385
7386 editor_handle.update(cx, |editor, cx| {
7387 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
7388 });
7389 let pane = workspace.active_pane().clone();
7390 pane.update(cx, |pane, _| pane.disable_history());
7391
7392 // We defer the pane interaction because we ourselves are a workspace item
7393 // and activating a new item causes the pane to call a method on us reentrantly,
7394 // which panics if we're on the stack.
7395 cx.defer(move |workspace, cx| {
7396 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
7397 let editor = workspace.open_project_item::<Self>(buffer, cx);
7398 editor.update(cx, |editor, cx| {
7399 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7400 s.select_ranges(ranges);
7401 });
7402 });
7403 }
7404
7405 pane.update(cx, |pane, _| pane.enable_history());
7406 });
7407 }
7408
7409 fn jump(
7410 workspace: &mut Workspace,
7411 path: ProjectPath,
7412 position: Point,
7413 anchor: language::Anchor,
7414 cx: &mut ViewContext<Workspace>,
7415 ) {
7416 let editor = workspace.open_path(path, None, true, cx);
7417 cx.spawn(|_, mut cx| async move {
7418 let editor = editor
7419 .await?
7420 .downcast::<Editor>()
7421 .ok_or_else(|| anyhow!("opened item was not an editor"))?
7422 .downgrade();
7423 editor.update(&mut cx, |editor, cx| {
7424 let buffer = editor
7425 .buffer()
7426 .read(cx)
7427 .as_singleton()
7428 .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
7429 let buffer = buffer.read(cx);
7430 let cursor = if buffer.can_resolve(&anchor) {
7431 language::ToPoint::to_point(&anchor, buffer)
7432 } else {
7433 buffer.clip_point(position, Bias::Left)
7434 };
7435
7436 let nav_history = editor.nav_history.take();
7437 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
7438 s.select_ranges([cursor..cursor]);
7439 });
7440 editor.nav_history = nav_history;
7441
7442 anyhow::Ok(())
7443 })??;
7444
7445 anyhow::Ok(())
7446 })
7447 .detach_and_log_err(cx);
7448 }
7449
7450 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
7451 let snapshot = self.buffer.read(cx).read(cx);
7452 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
7453 Some(
7454 ranges
7455 .iter()
7456 .map(move |range| {
7457 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
7458 })
7459 .collect(),
7460 )
7461 }
7462
7463 fn selection_replacement_ranges(
7464 &self,
7465 range: Range<OffsetUtf16>,
7466 cx: &AppContext,
7467 ) -> Vec<Range<OffsetUtf16>> {
7468 let selections = self.selections.all::<OffsetUtf16>(cx);
7469 let newest_selection = selections
7470 .iter()
7471 .max_by_key(|selection| selection.id)
7472 .unwrap();
7473 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
7474 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
7475 let snapshot = self.buffer.read(cx).read(cx);
7476 selections
7477 .into_iter()
7478 .map(|mut selection| {
7479 selection.start.0 =
7480 (selection.start.0 as isize).saturating_add(start_delta) as usize;
7481 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
7482 snapshot.clip_offset_utf16(selection.start, Bias::Left)
7483 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
7484 })
7485 .collect()
7486 }
7487
7488 fn report_copilot_event(
7489 &self,
7490 suggestion_id: Option<String>,
7491 suggestion_accepted: bool,
7492 cx: &AppContext,
7493 ) {
7494 let Some(project) = &self.project else {
7495 return
7496 };
7497
7498 // If None, we are either getting suggestions in a new, unsaved file, or in a file without an extension
7499 let file_extension = self
7500 .buffer
7501 .read(cx)
7502 .as_singleton()
7503 .and_then(|b| b.read(cx).file())
7504 .and_then(|file| Path::new(file.file_name(cx)).extension())
7505 .and_then(|e| e.to_str())
7506 .map(|a| a.to_string());
7507
7508 let telemetry = project.read(cx).client().telemetry().clone();
7509 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7510
7511 let event = ClickhouseEvent::Copilot {
7512 suggestion_id,
7513 suggestion_accepted,
7514 file_extension,
7515 };
7516 telemetry.report_clickhouse_event(event, telemetry_settings);
7517 }
7518
7519 fn report_editor_event(
7520 &self,
7521 name: &'static str,
7522 file_extension: Option<String>,
7523 cx: &AppContext,
7524 ) {
7525 let Some(project) = &self.project else {
7526 return
7527 };
7528
7529 // If None, we are in a file without an extension
7530 let file = self
7531 .buffer
7532 .read(cx)
7533 .as_singleton()
7534 .and_then(|b| b.read(cx).file());
7535 let file_extension = file_extension.or(file
7536 .as_ref()
7537 .and_then(|file| Path::new(file.file_name(cx)).extension())
7538 .and_then(|e| e.to_str())
7539 .map(|a| a.to_string()));
7540
7541 let vim_mode = cx
7542 .global::<SettingsStore>()
7543 .raw_user_settings()
7544 .get("vim_mode")
7545 == Some(&serde_json::Value::Bool(true));
7546 let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
7547 let copilot_enabled = all_language_settings(file, cx).copilot_enabled(None, None);
7548 let copilot_enabled_for_language = self
7549 .buffer
7550 .read(cx)
7551 .settings_at(0, cx)
7552 .show_copilot_suggestions;
7553
7554 let telemetry = project.read(cx).client().telemetry().clone();
7555 let event = ClickhouseEvent::Editor {
7556 file_extension,
7557 vim_mode,
7558 operation: name,
7559 copilot_enabled,
7560 copilot_enabled_for_language,
7561 };
7562 telemetry.report_clickhouse_event(event, telemetry_settings)
7563 }
7564
7565 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
7566 /// with each line being an array of {text, highlight} objects.
7567 fn copy_highlight_json(&mut self, _: &CopyHighlightJson, cx: &mut ViewContext<Self>) {
7568 let Some(buffer) = self.buffer.read(cx).as_singleton() else {
7569 return;
7570 };
7571
7572 #[derive(Serialize)]
7573 struct Chunk<'a> {
7574 text: String,
7575 highlight: Option<&'a str>,
7576 }
7577
7578 let snapshot = buffer.read(cx).snapshot();
7579 let range = self
7580 .selected_text_range(cx)
7581 .and_then(|selected_range| {
7582 if selected_range.is_empty() {
7583 None
7584 } else {
7585 Some(selected_range)
7586 }
7587 })
7588 .unwrap_or_else(|| 0..snapshot.len());
7589
7590 let chunks = snapshot.chunks(range, true);
7591 let mut lines = Vec::new();
7592 let mut line: VecDeque<Chunk> = VecDeque::new();
7593
7594 let theme = &theme::current(cx).editor.syntax;
7595
7596 for chunk in chunks {
7597 let highlight = chunk.syntax_highlight_id.and_then(|id| id.name(theme));
7598 let mut chunk_lines = chunk.text.split("\n").peekable();
7599 while let Some(text) = chunk_lines.next() {
7600 let mut merged_with_last_token = false;
7601 if let Some(last_token) = line.back_mut() {
7602 if last_token.highlight == highlight {
7603 last_token.text.push_str(text);
7604 merged_with_last_token = true;
7605 }
7606 }
7607
7608 if !merged_with_last_token {
7609 line.push_back(Chunk {
7610 text: text.into(),
7611 highlight,
7612 });
7613 }
7614
7615 if chunk_lines.peek().is_some() {
7616 if line.len() > 1 && line.front().unwrap().text.is_empty() {
7617 line.pop_front();
7618 }
7619 if line.len() > 1 && line.back().unwrap().text.is_empty() {
7620 line.pop_back();
7621 }
7622
7623 lines.push(mem::take(&mut line));
7624 }
7625 }
7626 }
7627
7628 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else { return; };
7629 cx.write_to_clipboard(ClipboardItem::new(lines));
7630 }
7631
7632 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
7633 &self.inlay_hint_cache
7634 }
7635}
7636
7637fn inlay_hint_settings(
7638 location: Anchor,
7639 snapshot: &MultiBufferSnapshot,
7640 cx: &mut ViewContext<'_, '_, Editor>,
7641) -> InlayHintSettings {
7642 let file = snapshot.file_at(location);
7643 let language = snapshot.language_at(location);
7644 let settings = all_language_settings(file, cx);
7645 settings
7646 .language(language.map(|l| l.name()).as_deref())
7647 .inlay_hints
7648}
7649
7650fn consume_contiguous_rows(
7651 contiguous_row_selections: &mut Vec<Selection<Point>>,
7652 selection: &Selection<Point>,
7653 display_map: &DisplaySnapshot,
7654 selections: &mut std::iter::Peekable<std::slice::Iter<Selection<Point>>>,
7655) -> (u32, u32) {
7656 contiguous_row_selections.push(selection.clone());
7657 let start_row = selection.start.row;
7658 let mut end_row = ending_row(selection, display_map);
7659
7660 while let Some(next_selection) = selections.peek() {
7661 if next_selection.start.row <= end_row {
7662 end_row = ending_row(next_selection, display_map);
7663 contiguous_row_selections.push(selections.next().unwrap().clone());
7664 } else {
7665 break;
7666 }
7667 }
7668 (start_row, end_row)
7669}
7670
7671fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> u32 {
7672 if next_selection.end.column > 0 || next_selection.is_empty() {
7673 display_map.next_line_boundary(next_selection.end).0.row + 1
7674 } else {
7675 next_selection.end.row
7676 }
7677}
7678
7679impl EditorSnapshot {
7680 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
7681 self.display_snapshot.buffer_snapshot.language_at(position)
7682 }
7683
7684 pub fn is_focused(&self) -> bool {
7685 self.is_focused
7686 }
7687
7688 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
7689 self.placeholder_text.as_ref()
7690 }
7691
7692 pub fn scroll_position(&self) -> Vector2F {
7693 self.scroll_anchor.scroll_position(&self.display_snapshot)
7694 }
7695}
7696
7697impl Deref for EditorSnapshot {
7698 type Target = DisplaySnapshot;
7699
7700 fn deref(&self) -> &Self::Target {
7701 &self.display_snapshot
7702 }
7703}
7704
7705#[derive(Clone, Debug, PartialEq, Eq)]
7706pub enum Event {
7707 InputIgnored {
7708 text: Arc<str>,
7709 },
7710 ExcerptsAdded {
7711 buffer: ModelHandle<Buffer>,
7712 predecessor: ExcerptId,
7713 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
7714 },
7715 ExcerptsRemoved {
7716 ids: Vec<ExcerptId>,
7717 },
7718 BufferEdited,
7719 Edited,
7720 Reparsed,
7721 Focused,
7722 Blurred,
7723 DirtyChanged,
7724 Saved,
7725 TitleChanged,
7726 DiffBaseChanged,
7727 SelectionsChanged {
7728 local: bool,
7729 },
7730 ScrollPositionChanged {
7731 local: bool,
7732 autoscroll: bool,
7733 },
7734 Closed,
7735}
7736
7737pub struct EditorFocused(pub ViewHandle<Editor>);
7738pub struct EditorBlurred(pub ViewHandle<Editor>);
7739pub struct EditorReleased(pub WeakViewHandle<Editor>);
7740
7741impl Entity for Editor {
7742 type Event = Event;
7743
7744 fn release(&mut self, cx: &mut AppContext) {
7745 cx.emit_global(EditorReleased(self.handle.clone()));
7746 }
7747}
7748
7749impl View for Editor {
7750 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
7751 let style = self.style(cx);
7752 let font_changed = self.display_map.update(cx, |map, cx| {
7753 map.set_fold_ellipses_color(style.folds.ellipses.text_color);
7754 map.set_font(style.text.font_id, style.text.font_size, cx)
7755 });
7756
7757 if font_changed {
7758 cx.defer(move |editor, cx: &mut ViewContext<Editor>| {
7759 hide_hover(editor, cx);
7760 hide_link_definition(editor, cx);
7761 });
7762 }
7763
7764 Stack::new()
7765 .with_child(EditorElement::new(style.clone()))
7766 .with_child(ChildView::new(&self.mouse_context_menu, cx))
7767 .into_any()
7768 }
7769
7770 fn ui_name() -> &'static str {
7771 "Editor"
7772 }
7773
7774 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7775 if cx.is_self_focused() {
7776 let focused_event = EditorFocused(cx.handle());
7777 cx.emit(Event::Focused);
7778 cx.emit_global(focused_event);
7779 }
7780 if let Some(rename) = self.pending_rename.as_ref() {
7781 cx.focus(&rename.editor);
7782 } else {
7783 if !self.focused {
7784 self.blink_manager.update(cx, BlinkManager::enable);
7785 }
7786 self.focused = true;
7787 self.buffer.update(cx, |buffer, cx| {
7788 buffer.finalize_last_transaction(cx);
7789 if self.leader_replica_id.is_none() {
7790 buffer.set_active_selections(
7791 &self.selections.disjoint_anchors(),
7792 self.selections.line_mode,
7793 self.cursor_shape,
7794 cx,
7795 );
7796 }
7797 });
7798 }
7799 }
7800
7801 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
7802 let blurred_event = EditorBlurred(cx.handle());
7803 cx.emit_global(blurred_event);
7804 self.focused = false;
7805 self.blink_manager.update(cx, BlinkManager::disable);
7806 self.buffer
7807 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
7808 self.hide_context_menu(cx);
7809 hide_hover(self, cx);
7810 cx.emit(Event::Blurred);
7811 cx.notify();
7812 }
7813
7814 fn modifiers_changed(
7815 &mut self,
7816 event: &gpui::platform::ModifiersChangedEvent,
7817 cx: &mut ViewContext<Self>,
7818 ) -> bool {
7819 let pending_selection = self.has_pending_selection();
7820
7821 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
7822 if event.cmd && !pending_selection {
7823 let snapshot = self.snapshot(cx);
7824 let kind = if event.shift {
7825 LinkDefinitionKind::Type
7826 } else {
7827 LinkDefinitionKind::Symbol
7828 };
7829
7830 show_link_definition(kind, self, point, snapshot, cx);
7831 return false;
7832 }
7833 }
7834
7835 {
7836 if self.link_go_to_definition_state.symbol_range.is_some()
7837 || !self.link_go_to_definition_state.definitions.is_empty()
7838 {
7839 self.link_go_to_definition_state.symbol_range.take();
7840 self.link_go_to_definition_state.definitions.clear();
7841 cx.notify();
7842 }
7843
7844 self.link_go_to_definition_state.task = None;
7845
7846 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
7847 }
7848
7849 false
7850 }
7851
7852 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
7853 Self::reset_to_default_keymap_context(keymap);
7854 let mode = match self.mode {
7855 EditorMode::SingleLine => "single_line",
7856 EditorMode::AutoHeight { .. } => "auto_height",
7857 EditorMode::Full => "full",
7858 };
7859 keymap.add_key("mode", mode);
7860 if self.pending_rename.is_some() {
7861 keymap.add_identifier("renaming");
7862 }
7863 match self.context_menu.as_ref() {
7864 Some(ContextMenu::Completions(_)) => {
7865 keymap.add_identifier("menu");
7866 keymap.add_identifier("showing_completions")
7867 }
7868 Some(ContextMenu::CodeActions(_)) => {
7869 keymap.add_identifier("menu");
7870 keymap.add_identifier("showing_code_actions")
7871 }
7872 None => {}
7873 }
7874 for layer in self.keymap_context_layers.values() {
7875 keymap.extend(layer);
7876 }
7877
7878 if let Some(extension) = self
7879 .buffer
7880 .read(cx)
7881 .as_singleton()
7882 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
7883 {
7884 keymap.add_key("extension", extension.to_string());
7885 }
7886 }
7887
7888 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
7889 Some(
7890 self.buffer
7891 .read(cx)
7892 .read(cx)
7893 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
7894 .collect(),
7895 )
7896 }
7897
7898 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7899 // Prevent the IME menu from appearing when holding down an alphabetic key
7900 // while input is disabled.
7901 if !self.input_enabled {
7902 return None;
7903 }
7904
7905 let range = self.selections.newest::<OffsetUtf16>(cx).range();
7906 Some(range.start.0..range.end.0)
7907 }
7908
7909 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
7910 let snapshot = self.buffer.read(cx).read(cx);
7911 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
7912 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
7913 }
7914
7915 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
7916 self.clear_text_highlights::<InputComposition>(cx);
7917 self.ime_transaction.take();
7918 }
7919
7920 fn replace_text_in_range(
7921 &mut self,
7922 range_utf16: Option<Range<usize>>,
7923 text: &str,
7924 cx: &mut ViewContext<Self>,
7925 ) {
7926 self.transact(cx, |this, cx| {
7927 if this.input_enabled {
7928 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
7929 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7930 Some(this.selection_replacement_ranges(range_utf16, cx))
7931 } else {
7932 this.marked_text_ranges(cx)
7933 };
7934
7935 if let Some(new_selected_ranges) = new_selected_ranges {
7936 this.change_selections(None, cx, |selections| {
7937 selections.select_ranges(new_selected_ranges)
7938 });
7939 }
7940 }
7941
7942 this.handle_input(text, cx);
7943 });
7944
7945 if !self.input_enabled {
7946 return;
7947 }
7948
7949 if let Some(transaction) = self.ime_transaction {
7950 self.buffer.update(cx, |buffer, cx| {
7951 buffer.group_until_transaction(transaction, cx);
7952 });
7953 }
7954
7955 self.unmark_text(cx);
7956 }
7957
7958 fn replace_and_mark_text_in_range(
7959 &mut self,
7960 range_utf16: Option<Range<usize>>,
7961 text: &str,
7962 new_selected_range_utf16: Option<Range<usize>>,
7963 cx: &mut ViewContext<Self>,
7964 ) {
7965 if !self.input_enabled {
7966 return;
7967 }
7968
7969 let transaction = self.transact(cx, |this, cx| {
7970 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
7971 let snapshot = this.buffer.read(cx).read(cx);
7972 if let Some(relative_range_utf16) = range_utf16.as_ref() {
7973 for marked_range in &mut marked_ranges {
7974 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
7975 marked_range.start.0 += relative_range_utf16.start;
7976 marked_range.start =
7977 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
7978 marked_range.end =
7979 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
7980 }
7981 }
7982 Some(marked_ranges)
7983 } else if let Some(range_utf16) = range_utf16 {
7984 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
7985 Some(this.selection_replacement_ranges(range_utf16, cx))
7986 } else {
7987 None
7988 };
7989
7990 if let Some(ranges) = ranges_to_replace {
7991 this.change_selections(None, cx, |s| s.select_ranges(ranges));
7992 }
7993
7994 let marked_ranges = {
7995 let snapshot = this.buffer.read(cx).read(cx);
7996 this.selections
7997 .disjoint_anchors()
7998 .iter()
7999 .map(|selection| {
8000 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8001 })
8002 .collect::<Vec<_>>()
8003 };
8004
8005 if text.is_empty() {
8006 this.unmark_text(cx);
8007 } else {
8008 this.highlight_text::<InputComposition>(
8009 marked_ranges.clone(),
8010 this.style(cx).composition_mark,
8011 cx,
8012 );
8013 }
8014
8015 this.handle_input(text, cx);
8016
8017 if let Some(new_selected_range) = new_selected_range_utf16 {
8018 let snapshot = this.buffer.read(cx).read(cx);
8019 let new_selected_ranges = marked_ranges
8020 .into_iter()
8021 .map(|marked_range| {
8022 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8023 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8024 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8025 snapshot.clip_offset_utf16(new_start, Bias::Left)
8026 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8027 })
8028 .collect::<Vec<_>>();
8029
8030 drop(snapshot);
8031 this.change_selections(None, cx, |selections| {
8032 selections.select_ranges(new_selected_ranges)
8033 });
8034 }
8035 });
8036
8037 self.ime_transaction = self.ime_transaction.or(transaction);
8038 if let Some(transaction) = self.ime_transaction {
8039 self.buffer.update(cx, |buffer, cx| {
8040 buffer.group_until_transaction(transaction, cx);
8041 });
8042 }
8043
8044 if self.text_highlights::<InputComposition>(cx).is_none() {
8045 self.ime_transaction.take();
8046 }
8047 }
8048}
8049
8050fn build_style(
8051 settings: &ThemeSettings,
8052 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8053 override_text_style: Option<&OverrideTextStyle>,
8054 cx: &AppContext,
8055) -> EditorStyle {
8056 let font_cache = cx.font_cache();
8057
8058 let theme_id = settings.theme.meta.id;
8059 let mut theme = settings.theme.editor.clone();
8060 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8061 let field_editor_theme = get_field_editor_theme(&settings.theme);
8062 theme.text_color = field_editor_theme.text.color;
8063 theme.selection = field_editor_theme.selection;
8064 theme.background = field_editor_theme
8065 .container
8066 .background_color
8067 .unwrap_or_default();
8068 EditorStyle {
8069 text: field_editor_theme.text,
8070 placeholder_text: field_editor_theme.placeholder_text,
8071 theme,
8072 theme_id,
8073 }
8074 } else {
8075 let font_family_id = settings.buffer_font_family;
8076 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8077 let font_properties = Default::default();
8078 let font_id = font_cache
8079 .select_font(font_family_id, &font_properties)
8080 .unwrap();
8081 let font_size = settings.buffer_font_size(cx);
8082 EditorStyle {
8083 text: TextStyle {
8084 color: settings.theme.editor.text_color,
8085 font_family_name,
8086 font_family_id,
8087 font_id,
8088 font_size,
8089 font_properties,
8090 underline: Default::default(),
8091 },
8092 placeholder_text: None,
8093 theme,
8094 theme_id,
8095 }
8096 };
8097
8098 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8099 if let Some(highlighted) = style
8100 .text
8101 .clone()
8102 .highlight(highlight_style, font_cache)
8103 .log_err()
8104 {
8105 style.text = highlighted;
8106 }
8107 }
8108
8109 style
8110}
8111
8112trait SelectionExt {
8113 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8114 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8115 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8116 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8117 -> Range<u32>;
8118}
8119
8120impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8121 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8122 let start = self.start.to_point(buffer);
8123 let end = self.end.to_point(buffer);
8124 if self.reversed {
8125 end..start
8126 } else {
8127 start..end
8128 }
8129 }
8130
8131 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8132 let start = self.start.to_offset(buffer);
8133 let end = self.end.to_offset(buffer);
8134 if self.reversed {
8135 end..start
8136 } else {
8137 start..end
8138 }
8139 }
8140
8141 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8142 let start = self
8143 .start
8144 .to_point(&map.buffer_snapshot)
8145 .to_display_point(map);
8146 let end = self
8147 .end
8148 .to_point(&map.buffer_snapshot)
8149 .to_display_point(map);
8150 if self.reversed {
8151 end..start
8152 } else {
8153 start..end
8154 }
8155 }
8156
8157 fn spanned_rows(
8158 &self,
8159 include_end_if_at_line_start: bool,
8160 map: &DisplaySnapshot,
8161 ) -> Range<u32> {
8162 let start = self.start.to_point(&map.buffer_snapshot);
8163 let mut end = self.end.to_point(&map.buffer_snapshot);
8164 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8165 end.row -= 1;
8166 }
8167
8168 let buffer_start = map.prev_line_boundary(start).0;
8169 let buffer_end = map.next_line_boundary(end).0;
8170 buffer_start.row..buffer_end.row + 1
8171 }
8172}
8173
8174impl<T: InvalidationRegion> InvalidationStack<T> {
8175 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8176 where
8177 S: Clone + ToOffset,
8178 {
8179 while let Some(region) = self.last() {
8180 let all_selections_inside_invalidation_ranges =
8181 if selections.len() == region.ranges().len() {
8182 selections
8183 .iter()
8184 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8185 .all(|(selection, invalidation_range)| {
8186 let head = selection.head().to_offset(buffer);
8187 invalidation_range.start <= head && invalidation_range.end >= head
8188 })
8189 } else {
8190 false
8191 };
8192
8193 if all_selections_inside_invalidation_ranges {
8194 break;
8195 } else {
8196 self.pop();
8197 }
8198 }
8199 }
8200}
8201
8202impl<T> Default for InvalidationStack<T> {
8203 fn default() -> Self {
8204 Self(Default::default())
8205 }
8206}
8207
8208impl<T> Deref for InvalidationStack<T> {
8209 type Target = Vec<T>;
8210
8211 fn deref(&self) -> &Self::Target {
8212 &self.0
8213 }
8214}
8215
8216impl<T> DerefMut for InvalidationStack<T> {
8217 fn deref_mut(&mut self) -> &mut Self::Target {
8218 &mut self.0
8219 }
8220}
8221
8222impl InvalidationRegion for SnippetState {
8223 fn ranges(&self) -> &[Range<Anchor>] {
8224 &self.ranges[self.active_index]
8225 }
8226}
8227
8228impl Deref for EditorStyle {
8229 type Target = theme::Editor;
8230
8231 fn deref(&self) -> &Self::Target {
8232 &self.theme
8233 }
8234}
8235
8236pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8237 let mut highlighted_lines = Vec::new();
8238
8239 for (index, line) in diagnostic.message.lines().enumerate() {
8240 let line = match &diagnostic.source {
8241 Some(source) if index == 0 => {
8242 let source_highlight = Vec::from_iter(0..source.len());
8243 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8244 }
8245
8246 _ => highlight_diagnostic_message(Vec::new(), line),
8247 };
8248 highlighted_lines.push(line);
8249 }
8250 let message = diagnostic.message;
8251 Arc::new(move |cx: &mut BlockContext| {
8252 let message = message.clone();
8253 let settings = settings::get::<ThemeSettings>(cx);
8254 let tooltip_style = settings.theme.tooltip.clone();
8255 let theme = &settings.theme.editor;
8256 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8257 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8258 let anchor_x = cx.anchor_x;
8259 enum BlockContextToolip {}
8260 MouseEventHandler::<BlockContext, _>::new(cx.block_id, cx, |_, _| {
8261 Flex::column()
8262 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8263 Label::new(
8264 line.clone(),
8265 style.message.clone().with_font_size(font_size),
8266 )
8267 .with_highlights(highlights.clone())
8268 .contained()
8269 .with_margin_left(anchor_x)
8270 }))
8271 .aligned()
8272 .left()
8273 .into_any()
8274 })
8275 .with_cursor_style(CursorStyle::PointingHand)
8276 .on_click(MouseButton::Left, move |_, _, cx| {
8277 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8278 })
8279 // We really need to rethink this ID system...
8280 .with_tooltip::<BlockContextToolip>(
8281 cx.block_id,
8282 "Copy diagnostic message".to_string(),
8283 None,
8284 tooltip_style,
8285 cx,
8286 )
8287 .into_any()
8288 })
8289}
8290
8291pub fn highlight_diagnostic_message(
8292 initial_highlights: Vec<usize>,
8293 message: &str,
8294) -> (String, Vec<usize>) {
8295 let mut message_without_backticks = String::new();
8296 let mut prev_offset = 0;
8297 let mut inside_block = false;
8298 let mut highlights = initial_highlights;
8299 for (match_ix, (offset, _)) in message
8300 .match_indices('`')
8301 .chain([(message.len(), "")])
8302 .enumerate()
8303 {
8304 message_without_backticks.push_str(&message[prev_offset..offset]);
8305 if inside_block {
8306 highlights.extend(prev_offset - match_ix..offset - match_ix);
8307 }
8308
8309 inside_block = !inside_block;
8310 prev_offset = offset + 1;
8311 }
8312
8313 (message_without_backticks, highlights)
8314}
8315
8316pub fn diagnostic_style(
8317 severity: DiagnosticSeverity,
8318 valid: bool,
8319 theme: &theme::Editor,
8320) -> DiagnosticStyle {
8321 match (severity, valid) {
8322 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8323 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8324 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8325 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8326 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8327 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8328 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8329 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8330 _ => theme.invalid_hint_diagnostic.clone(),
8331 }
8332}
8333
8334pub fn combine_syntax_and_fuzzy_match_highlights(
8335 text: &str,
8336 default_style: HighlightStyle,
8337 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8338 match_indices: &[usize],
8339) -> Vec<(Range<usize>, HighlightStyle)> {
8340 let mut result = Vec::new();
8341 let mut match_indices = match_indices.iter().copied().peekable();
8342
8343 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8344 {
8345 syntax_highlight.weight = None;
8346
8347 // Add highlights for any fuzzy match characters before the next
8348 // syntax highlight range.
8349 while let Some(&match_index) = match_indices.peek() {
8350 if match_index >= range.start {
8351 break;
8352 }
8353 match_indices.next();
8354 let end_index = char_ix_after(match_index, text);
8355 let mut match_style = default_style;
8356 match_style.weight = Some(fonts::Weight::BOLD);
8357 result.push((match_index..end_index, match_style));
8358 }
8359
8360 if range.start == usize::MAX {
8361 break;
8362 }
8363
8364 // Add highlights for any fuzzy match characters within the
8365 // syntax highlight range.
8366 let mut offset = range.start;
8367 while let Some(&match_index) = match_indices.peek() {
8368 if match_index >= range.end {
8369 break;
8370 }
8371
8372 match_indices.next();
8373 if match_index > offset {
8374 result.push((offset..match_index, syntax_highlight));
8375 }
8376
8377 let mut end_index = char_ix_after(match_index, text);
8378 while let Some(&next_match_index) = match_indices.peek() {
8379 if next_match_index == end_index && next_match_index < range.end {
8380 end_index = char_ix_after(next_match_index, text);
8381 match_indices.next();
8382 } else {
8383 break;
8384 }
8385 }
8386
8387 let mut match_style = syntax_highlight;
8388 match_style.weight = Some(fonts::Weight::BOLD);
8389 result.push((match_index..end_index, match_style));
8390 offset = end_index;
8391 }
8392
8393 if offset < range.end {
8394 result.push((offset..range.end, syntax_highlight));
8395 }
8396 }
8397
8398 fn char_ix_after(ix: usize, text: &str) -> usize {
8399 ix + text[ix..].chars().next().unwrap().len_utf8()
8400 }
8401
8402 result
8403}
8404
8405pub fn styled_runs_for_code_label<'a>(
8406 label: &'a CodeLabel,
8407 syntax_theme: &'a theme::SyntaxTheme,
8408) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8409 let fade_out = HighlightStyle {
8410 fade_out: Some(0.35),
8411 ..Default::default()
8412 };
8413
8414 let mut prev_end = label.filter_range.end;
8415 label
8416 .runs
8417 .iter()
8418 .enumerate()
8419 .flat_map(move |(ix, (range, highlight_id))| {
8420 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8421 style
8422 } else {
8423 return Default::default();
8424 };
8425 let mut muted_style = style;
8426 muted_style.highlight(fade_out);
8427
8428 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8429 if range.start >= label.filter_range.end {
8430 if range.start > prev_end {
8431 runs.push((prev_end..range.start, fade_out));
8432 }
8433 runs.push((range.clone(), muted_style));
8434 } else if range.end <= label.filter_range.end {
8435 runs.push((range.clone(), style));
8436 } else {
8437 runs.push((range.start..label.filter_range.end, style));
8438 runs.push((label.filter_range.end..range.end, muted_style));
8439 }
8440 prev_end = cmp::max(prev_end, range.end);
8441
8442 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8443 runs.push((prev_end..label.text.len(), fade_out));
8444 }
8445
8446 runs
8447 })
8448}
8449
8450pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8451 let mut index = 0;
8452 let mut codepoints = text.char_indices().peekable();
8453
8454 std::iter::from_fn(move || {
8455 let start_index = index;
8456 while let Some((new_index, codepoint)) = codepoints.next() {
8457 index = new_index + codepoint.len_utf8();
8458 let current_upper = codepoint.is_uppercase();
8459 let next_upper = codepoints
8460 .peek()
8461 .map(|(_, c)| c.is_uppercase())
8462 .unwrap_or(false);
8463
8464 if !current_upper && next_upper {
8465 return Some(&text[start_index..index]);
8466 }
8467 }
8468
8469 index = text.len();
8470 if start_index < text.len() {
8471 return Some(&text[start_index..]);
8472 }
8473 None
8474 })
8475 .flat_map(|word| word.split_inclusive('_'))
8476}
8477
8478trait RangeToAnchorExt {
8479 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8480}
8481
8482impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8483 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8484 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8485 }
8486}