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