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