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