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