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