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