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