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