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