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