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