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