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