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