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