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