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