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, ResultExt, TryFutureExt, RangeExt};
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 { return; };
4794
4795 let mut best_length = usize::MAX;
4796 let mut best_inside = false;
4797 let mut best_in_bracket_range = false;
4798 let mut best_destination = None;
4799 for (open, close) in enclosing_bracket_ranges {
4800 let close = close.to_inclusive();
4801 let length = close.end() - open.start;
4802 let inside = selection.start >= open.end && selection.end <= *close.start();
4803 let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head());
4804
4805 // If best is next to a bracket and current isn't, skip
4806 if !in_bracket_range && best_in_bracket_range {
4807 continue;
4808 }
4809
4810 // Prefer smaller lengths unless best is inside and current isn't
4811 if length > best_length && (best_inside || !inside) {
4812 continue;
4813 }
4814
4815 best_length = length;
4816 best_inside = inside;
4817 best_in_bracket_range = in_bracket_range;
4818 best_destination = Some(if close.contains(&selection.start) && close.contains(&selection.end) {
4819 if inside {
4820 open.end
4821 } else {
4822 open.start
4823 }
4824 } else {
4825 if inside {
4826 *close.start()
4827 } else {
4828 *close.end()
4829 }
4830 });
4831 }
4832
4833 if let Some(destination) = best_destination {
4834 selection.collapse_to(destination, SelectionGoal::None);
4835 }
4836 })
4837 });
4838 }
4839
4840 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4841 self.end_selection(cx);
4842 self.selection_history.mode = SelectionHistoryMode::Undoing;
4843 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4844 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4845 self.select_next_state = entry.select_next_state;
4846 self.add_selections_state = entry.add_selections_state;
4847 self.request_autoscroll(Autoscroll::newest(), cx);
4848 }
4849 self.selection_history.mode = SelectionHistoryMode::Normal;
4850 }
4851
4852 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4853 self.end_selection(cx);
4854 self.selection_history.mode = SelectionHistoryMode::Redoing;
4855 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4856 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4857 self.select_next_state = entry.select_next_state;
4858 self.add_selections_state = entry.add_selections_state;
4859 self.request_autoscroll(Autoscroll::newest(), cx);
4860 }
4861 self.selection_history.mode = SelectionHistoryMode::Normal;
4862 }
4863
4864 fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
4865 self.go_to_diagnostic_impl(Direction::Next, cx)
4866 }
4867
4868 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4869 self.go_to_diagnostic_impl(Direction::Prev, cx)
4870 }
4871
4872 pub fn go_to_diagnostic_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4873 let buffer = self.buffer.read(cx).snapshot(cx);
4874 let selection = self.selections.newest::<usize>(cx);
4875
4876 // If there is an active Diagnostic Popover. Jump to it's diagnostic instead.
4877 if direction == Direction::Next {
4878 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
4879 let (group_id, jump_to) = popover.activation_info();
4880 if self.activate_diagnostics(group_id, cx) {
4881 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4882 let mut new_selection = s.newest_anchor().clone();
4883 new_selection.collapse_to(jump_to, SelectionGoal::None);
4884 s.select_anchors(vec![new_selection.clone()]);
4885 });
4886 }
4887 return;
4888 }
4889 }
4890
4891 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4892 active_diagnostics
4893 .primary_range
4894 .to_offset(&buffer)
4895 .to_inclusive()
4896 });
4897 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4898 if active_primary_range.contains(&selection.head()) {
4899 *active_primary_range.end()
4900 } else {
4901 selection.head()
4902 }
4903 } else {
4904 selection.head()
4905 };
4906
4907 loop {
4908 let mut diagnostics = if direction == Direction::Prev {
4909 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4910 } else {
4911 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4912 };
4913 let group = diagnostics.find_map(|entry| {
4914 if entry.diagnostic.is_primary
4915 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4916 && !entry.range.is_empty()
4917 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4918 {
4919 Some((entry.range, entry.diagnostic.group_id))
4920 } else {
4921 None
4922 }
4923 });
4924
4925 if let Some((primary_range, group_id)) = group {
4926 if self.activate_diagnostics(group_id, cx) {
4927 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
4928 s.select(vec![Selection {
4929 id: selection.id,
4930 start: primary_range.start,
4931 end: primary_range.start,
4932 reversed: false,
4933 goal: SelectionGoal::None,
4934 }]);
4935 });
4936 }
4937 break;
4938 } else {
4939 // Cycle around to the start of the buffer, potentially moving back to the start of
4940 // the currently active diagnostic.
4941 active_primary_range.take();
4942 if direction == Direction::Prev {
4943 if search_start == buffer.len() {
4944 break;
4945 } else {
4946 search_start = buffer.len();
4947 }
4948 } else if search_start == 0 {
4949 break;
4950 } else {
4951 search_start = 0;
4952 }
4953 }
4954 }
4955 }
4956
4957 fn go_to_hunk(&mut self, _: &GoToHunk, cx: &mut ViewContext<Self>) {
4958 self.go_to_hunk_impl(Direction::Next, cx)
4959 }
4960
4961 fn go_to_prev_hunk(&mut self, _: &GoToPrevHunk, cx: &mut ViewContext<Self>) {
4962 self.go_to_hunk_impl(Direction::Prev, cx)
4963 }
4964
4965 pub fn go_to_hunk_impl(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4966 let snapshot = self
4967 .display_map
4968 .update(cx, |display_map, cx| display_map.snapshot(cx));
4969 let selection = self.selections.newest::<Point>(cx);
4970
4971 fn seek_in_direction(
4972 this: &mut Editor,
4973 snapshot: &DisplaySnapshot,
4974 initial_point: Point,
4975 is_wrapped: bool,
4976 direction: Direction,
4977 cx: &mut ViewContext<Editor>,
4978 ) -> bool {
4979 let hunks = if direction == Direction::Next {
4980 snapshot
4981 .buffer_snapshot
4982 .git_diff_hunks_in_range(initial_point.row..u32::MAX, false)
4983 } else {
4984 snapshot
4985 .buffer_snapshot
4986 .git_diff_hunks_in_range(0..initial_point.row, true)
4987 };
4988
4989 let display_point = initial_point.to_display_point(snapshot);
4990 let mut hunks = hunks
4991 .map(|hunk| diff_hunk_to_display(hunk, &snapshot))
4992 .skip_while(|hunk| {
4993 if is_wrapped {
4994 false
4995 } else {
4996 hunk.contains_display_row(display_point.row())
4997 }
4998 })
4999 .dedup();
5000
5001 if let Some(hunk) = hunks.next() {
5002 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
5003 let row = hunk.start_display_row();
5004 let point = DisplayPoint::new(row, 0);
5005 s.select_display_ranges([point..point]);
5006 });
5007
5008 true
5009 } else {
5010 false
5011 }
5012 }
5013
5014 if !seek_in_direction(self, &snapshot, selection.head(), false, direction, cx) {
5015 let wrapped_point = match direction {
5016 Direction::Next => Point::zero(),
5017 Direction::Prev => snapshot.buffer_snapshot.max_point(),
5018 };
5019 seek_in_direction(self, &snapshot, wrapped_point, true, direction, cx);
5020 }
5021 }
5022
5023 pub fn go_to_definition(
5024 workspace: &mut Workspace,
5025 _: &GoToDefinition,
5026 cx: &mut ViewContext<Workspace>,
5027 ) {
5028 Self::go_to_definition_of_kind(GotoDefinitionKind::Symbol, workspace, cx);
5029 }
5030
5031 pub fn go_to_type_definition(
5032 workspace: &mut Workspace,
5033 _: &GoToTypeDefinition,
5034 cx: &mut ViewContext<Workspace>,
5035 ) {
5036 Self::go_to_definition_of_kind(GotoDefinitionKind::Type, workspace, cx);
5037 }
5038
5039 fn go_to_definition_of_kind(
5040 kind: GotoDefinitionKind,
5041 workspace: &mut Workspace,
5042 cx: &mut ViewContext<Workspace>,
5043 ) {
5044 let active_item = workspace.active_item(cx);
5045 let editor_handle = if let Some(editor) = active_item
5046 .as_ref()
5047 .and_then(|item| item.act_as::<Self>(cx))
5048 {
5049 editor
5050 } else {
5051 return;
5052 };
5053
5054 let editor = editor_handle.read(cx);
5055 let buffer = editor.buffer.read(cx);
5056 let head = editor.selections.newest::<usize>(cx).head();
5057 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
5058 text_anchor
5059 } else {
5060 return;
5061 };
5062
5063 let project = workspace.project().clone();
5064 let definitions = project.update(cx, |project, cx| match kind {
5065 GotoDefinitionKind::Symbol => project.definition(&buffer, head, cx),
5066 GotoDefinitionKind::Type => project.type_definition(&buffer, head, cx),
5067 });
5068
5069 cx.spawn(|workspace, mut cx| async move {
5070 let definitions = definitions.await?;
5071 workspace.update(&mut cx, |workspace, cx| {
5072 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
5073 });
5074
5075 Ok::<(), anyhow::Error>(())
5076 })
5077 .detach_and_log_err(cx);
5078 }
5079
5080 pub fn navigate_to_definitions(
5081 workspace: &mut Workspace,
5082 editor_handle: ViewHandle<Editor>,
5083 definitions: Vec<LocationLink>,
5084 cx: &mut ViewContext<Workspace>,
5085 ) {
5086 let pane = workspace.active_pane().clone();
5087 // If there is one definition, just open it directly
5088 if let [definition] = definitions.as_slice() {
5089 let range = definition
5090 .target
5091 .range
5092 .to_offset(definition.target.buffer.read(cx));
5093
5094 let target_editor_handle =
5095 workspace.open_project_item(definition.target.buffer.clone(), cx);
5096 target_editor_handle.update(cx, |target_editor, cx| {
5097 // When selecting a definition in a different buffer, disable the nav history
5098 // to avoid creating a history entry at the previous cursor location.
5099 if editor_handle != target_editor_handle {
5100 pane.update(cx, |pane, _| pane.disable_history());
5101 }
5102 target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
5103 s.select_ranges([range]);
5104 });
5105
5106 pane.update(cx, |pane, _| pane.enable_history());
5107 });
5108 } else if !definitions.is_empty() {
5109 let replica_id = editor_handle.read(cx).replica_id(cx);
5110 let title = definitions
5111 .iter()
5112 .find(|definition| definition.origin.is_some())
5113 .and_then(|definition| {
5114 definition.origin.as_ref().map(|origin| {
5115 let buffer = origin.buffer.read(cx);
5116 format!(
5117 "Definitions for {}",
5118 buffer
5119 .text_for_range(origin.range.clone())
5120 .collect::<String>()
5121 )
5122 })
5123 })
5124 .unwrap_or("Definitions".to_owned());
5125 let locations = definitions
5126 .into_iter()
5127 .map(|definition| definition.target)
5128 .collect();
5129 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx)
5130 }
5131 }
5132
5133 pub fn find_all_references(
5134 workspace: &mut Workspace,
5135 _: &FindAllReferences,
5136 cx: &mut ViewContext<Workspace>,
5137 ) -> Option<Task<Result<()>>> {
5138 let active_item = workspace.active_item(cx)?;
5139 let editor_handle = active_item.act_as::<Self>(cx)?;
5140
5141 let editor = editor_handle.read(cx);
5142 let buffer = editor.buffer.read(cx);
5143 let head = editor.selections.newest::<usize>(cx).head();
5144 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
5145 let replica_id = editor.replica_id(cx);
5146
5147 let project = workspace.project().clone();
5148 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
5149 Some(cx.spawn(|workspace, mut cx| async move {
5150 let locations = references.await?;
5151 if locations.is_empty() {
5152 return Ok(());
5153 }
5154
5155 workspace.update(&mut cx, |workspace, cx| {
5156 let title = locations
5157 .first()
5158 .as_ref()
5159 .map(|location| {
5160 let buffer = location.buffer.read(cx);
5161 format!(
5162 "References to `{}`",
5163 buffer
5164 .text_for_range(location.range.clone())
5165 .collect::<String>()
5166 )
5167 })
5168 .unwrap();
5169 Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx);
5170 });
5171
5172 Ok(())
5173 }))
5174 }
5175
5176 /// Opens a multibuffer with the given project locations in it
5177 pub fn open_locations_in_multibuffer(
5178 workspace: &mut Workspace,
5179 mut locations: Vec<Location>,
5180 replica_id: ReplicaId,
5181 title: String,
5182 cx: &mut ViewContext<Workspace>,
5183 ) {
5184 // If there are multiple definitions, open them in a multibuffer
5185 locations.sort_by_key(|location| location.buffer.id());
5186 let mut locations = locations.into_iter().peekable();
5187 let mut ranges_to_highlight = Vec::new();
5188
5189 let excerpt_buffer = cx.add_model(|cx| {
5190 let mut multibuffer = MultiBuffer::new(replica_id);
5191 while let Some(location) = locations.next() {
5192 let buffer = location.buffer.read(cx);
5193 let mut ranges_for_buffer = Vec::new();
5194 let range = location.range.to_offset(buffer);
5195 ranges_for_buffer.push(range.clone());
5196
5197 while let Some(next_location) = locations.peek() {
5198 if next_location.buffer == location.buffer {
5199 ranges_for_buffer.push(next_location.range.to_offset(buffer));
5200 locations.next();
5201 } else {
5202 break;
5203 }
5204 }
5205
5206 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
5207 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
5208 location.buffer.clone(),
5209 ranges_for_buffer,
5210 1,
5211 cx,
5212 ))
5213 }
5214
5215 multibuffer.with_title(title)
5216 });
5217
5218 let editor = cx.add_view(|cx| {
5219 Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), cx)
5220 });
5221 editor.update(cx, |editor, cx| {
5222 editor.highlight_background::<Self>(
5223 ranges_to_highlight,
5224 |theme| theme.editor.highlighted_line_background,
5225 cx,
5226 );
5227 });
5228 workspace.add_item(Box::new(editor), cx);
5229 }
5230
5231 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
5232 use language::ToOffset as _;
5233
5234 let project = self.project.clone()?;
5235 let selection = self.selections.newest_anchor().clone();
5236 let (cursor_buffer, cursor_buffer_position) = self
5237 .buffer
5238 .read(cx)
5239 .text_anchor_for_position(selection.head(), cx)?;
5240 let (tail_buffer, _) = self
5241 .buffer
5242 .read(cx)
5243 .text_anchor_for_position(selection.tail(), cx)?;
5244 if tail_buffer != cursor_buffer {
5245 return None;
5246 }
5247
5248 let snapshot = cursor_buffer.read(cx).snapshot();
5249 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
5250 let prepare_rename = project.update(cx, |project, cx| {
5251 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
5252 });
5253
5254 Some(cx.spawn(|this, mut cx| async move {
5255 let rename_range = if let Some(range) = prepare_rename.await? {
5256 Some(range)
5257 } else {
5258 this.read_with(&cx, |this, cx| {
5259 let buffer = this.buffer.read(cx).snapshot(cx);
5260 let mut buffer_highlights = this
5261 .document_highlights_for_position(selection.head(), &buffer)
5262 .filter(|highlight| {
5263 highlight.start.excerpt_id() == selection.head().excerpt_id()
5264 && highlight.end.excerpt_id() == selection.head().excerpt_id()
5265 });
5266 buffer_highlights
5267 .next()
5268 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
5269 })
5270 };
5271 if let Some(rename_range) = rename_range {
5272 let rename_buffer_range = rename_range.to_offset(&snapshot);
5273 let cursor_offset_in_rename_range =
5274 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
5275
5276 this.update(&mut cx, |this, cx| {
5277 this.take_rename(false, cx);
5278 let style = this.style(cx);
5279 let buffer = this.buffer.read(cx).read(cx);
5280 let cursor_offset = selection.head().to_offset(&buffer);
5281 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
5282 let rename_end = rename_start + rename_buffer_range.len();
5283 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
5284 let mut old_highlight_id = None;
5285 let old_name: Arc<str> = buffer
5286 .chunks(rename_start..rename_end, true)
5287 .map(|chunk| {
5288 if old_highlight_id.is_none() {
5289 old_highlight_id = chunk.syntax_highlight_id;
5290 }
5291 chunk.text
5292 })
5293 .collect::<String>()
5294 .into();
5295
5296 drop(buffer);
5297
5298 // Position the selection in the rename editor so that it matches the current selection.
5299 this.show_local_selections = false;
5300 let rename_editor = cx.add_view(|cx| {
5301 let mut editor = Editor::single_line(None, cx);
5302 if let Some(old_highlight_id) = old_highlight_id {
5303 editor.override_text_style =
5304 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
5305 }
5306 editor.buffer.update(cx, |buffer, cx| {
5307 buffer.edit([(0..0, old_name.clone())], None, cx)
5308 });
5309 editor.select_all(&SelectAll, cx);
5310 editor
5311 });
5312
5313 let ranges = this
5314 .clear_background_highlights::<DocumentHighlightWrite>(cx)
5315 .into_iter()
5316 .flat_map(|(_, ranges)| ranges)
5317 .chain(
5318 this.clear_background_highlights::<DocumentHighlightRead>(cx)
5319 .into_iter()
5320 .flat_map(|(_, ranges)| ranges),
5321 )
5322 .collect();
5323
5324 this.highlight_text::<Rename>(
5325 ranges,
5326 HighlightStyle {
5327 fade_out: Some(style.rename_fade),
5328 ..Default::default()
5329 },
5330 cx,
5331 );
5332 cx.focus(&rename_editor);
5333 let block_id = this.insert_blocks(
5334 [BlockProperties {
5335 style: BlockStyle::Flex,
5336 position: range.start.clone(),
5337 height: 1,
5338 render: Arc::new({
5339 let editor = rename_editor.clone();
5340 move |cx: &mut BlockContext| {
5341 ChildView::new(editor.clone(), cx)
5342 .contained()
5343 .with_padding_left(cx.anchor_x)
5344 .boxed()
5345 }
5346 }),
5347 disposition: BlockDisposition::Below,
5348 }],
5349 cx,
5350 )[0];
5351 this.pending_rename = Some(RenameState {
5352 range,
5353 old_name,
5354 editor: rename_editor,
5355 block_id,
5356 });
5357 });
5358 }
5359
5360 Ok(())
5361 }))
5362 }
5363
5364 pub fn confirm_rename(
5365 workspace: &mut Workspace,
5366 _: &ConfirmRename,
5367 cx: &mut ViewContext<Workspace>,
5368 ) -> Option<Task<Result<()>>> {
5369 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
5370
5371 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
5372 let rename = editor.take_rename(false, cx)?;
5373 let buffer = editor.buffer.read(cx);
5374 let (start_buffer, start) =
5375 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
5376 let (end_buffer, end) =
5377 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
5378 if start_buffer == end_buffer {
5379 let new_name = rename.editor.read(cx).text(cx);
5380 Some((start_buffer, start..end, rename.old_name, new_name))
5381 } else {
5382 None
5383 }
5384 })?;
5385
5386 let rename = workspace.project().clone().update(cx, |project, cx| {
5387 project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
5388 });
5389
5390 Some(cx.spawn(|workspace, mut cx| async move {
5391 let project_transaction = rename.await?;
5392 Self::open_project_transaction(
5393 editor.clone(),
5394 workspace,
5395 project_transaction,
5396 format!("Rename: {} → {}", old_name, new_name),
5397 cx.clone(),
5398 )
5399 .await?;
5400
5401 editor.update(&mut cx, |editor, cx| {
5402 editor.refresh_document_highlights(cx);
5403 });
5404 Ok(())
5405 }))
5406 }
5407
5408 fn take_rename(
5409 &mut self,
5410 moving_cursor: bool,
5411 cx: &mut ViewContext<Self>,
5412 ) -> Option<RenameState> {
5413 let rename = self.pending_rename.take()?;
5414 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
5415 self.clear_text_highlights::<Rename>(cx);
5416 self.show_local_selections = true;
5417
5418 if moving_cursor {
5419 let rename_editor = rename.editor.read(cx);
5420 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
5421
5422 // Update the selection to match the position of the selection inside
5423 // the rename editor.
5424 let snapshot = self.buffer.read(cx).read(cx);
5425 let rename_range = rename.range.to_offset(&snapshot);
5426 let cursor_in_editor = snapshot
5427 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
5428 .min(rename_range.end);
5429 drop(snapshot);
5430
5431 self.change_selections(None, cx, |s| {
5432 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
5433 });
5434 } else {
5435 self.refresh_document_highlights(cx);
5436 }
5437
5438 Some(rename)
5439 }
5440
5441 #[cfg(any(test, feature = "test-support"))]
5442 pub fn pending_rename(&self) -> Option<&RenameState> {
5443 self.pending_rename.as_ref()
5444 }
5445
5446 fn format(&mut self, _: &Format, cx: &mut ViewContext<'_, Self>) -> Option<Task<Result<()>>> {
5447 let project = match &self.project {
5448 Some(project) => project.clone(),
5449 None => return None,
5450 };
5451
5452 Some(self.perform_format(project, cx))
5453 }
5454
5455 fn perform_format(
5456 &mut self,
5457 project: ModelHandle<Project>,
5458 cx: &mut ViewContext<'_, Self>,
5459 ) -> Task<Result<()>> {
5460 let buffer = self.buffer().clone();
5461 let buffers = buffer.read(cx).all_buffers();
5462
5463 let mut timeout = cx.background().timer(FORMAT_TIMEOUT).fuse();
5464 let format = project.update(cx, |project, cx| {
5465 project.format(buffers, true, FormatTrigger::Manual, cx)
5466 });
5467
5468 cx.spawn(|_, mut cx| async move {
5469 let transaction = futures::select_biased! {
5470 _ = timeout => {
5471 log::warn!("timed out waiting for formatting");
5472 None
5473 }
5474 transaction = format.log_err().fuse() => transaction,
5475 };
5476
5477 buffer.update(&mut cx, |buffer, cx| {
5478 if let Some(transaction) = transaction {
5479 if !buffer.is_singleton() {
5480 buffer.push_transaction(&transaction.0);
5481 }
5482 }
5483
5484 cx.notify();
5485 });
5486
5487 Ok(())
5488 })
5489 }
5490
5491 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
5492 if let Some(project) = self.project.clone() {
5493 self.buffer.update(cx, |multi_buffer, cx| {
5494 project.update(cx, |project, cx| {
5495 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
5496 });
5497 })
5498 }
5499 }
5500
5501 fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
5502 cx.show_character_palette();
5503 }
5504
5505 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5506 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5507 let buffer = self.buffer.read(cx).snapshot(cx);
5508 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5509 let is_valid = buffer
5510 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5511 .any(|entry| {
5512 entry.diagnostic.is_primary
5513 && !entry.range.is_empty()
5514 && entry.range.start == primary_range_start
5515 && entry.diagnostic.message == active_diagnostics.primary_message
5516 });
5517
5518 if is_valid != active_diagnostics.is_valid {
5519 active_diagnostics.is_valid = is_valid;
5520 let mut new_styles = HashMap::default();
5521 for (block_id, diagnostic) in &active_diagnostics.blocks {
5522 new_styles.insert(
5523 *block_id,
5524 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5525 );
5526 }
5527 self.display_map
5528 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5529 }
5530 }
5531 }
5532
5533 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) -> bool {
5534 self.dismiss_diagnostics(cx);
5535 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5536 let buffer = self.buffer.read(cx).snapshot(cx);
5537
5538 let mut primary_range = None;
5539 let mut primary_message = None;
5540 let mut group_end = Point::zero();
5541 let diagnostic_group = buffer
5542 .diagnostic_group::<Point>(group_id)
5543 .map(|entry| {
5544 if entry.range.end > group_end {
5545 group_end = entry.range.end;
5546 }
5547 if entry.diagnostic.is_primary {
5548 primary_range = Some(entry.range.clone());
5549 primary_message = Some(entry.diagnostic.message.clone());
5550 }
5551 entry
5552 })
5553 .collect::<Vec<_>>();
5554 let primary_range = primary_range?;
5555 let primary_message = primary_message?;
5556 let primary_range =
5557 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5558
5559 let blocks = display_map
5560 .insert_blocks(
5561 diagnostic_group.iter().map(|entry| {
5562 let diagnostic = entry.diagnostic.clone();
5563 let message_height = diagnostic.message.lines().count() as u8;
5564 BlockProperties {
5565 style: BlockStyle::Fixed,
5566 position: buffer.anchor_after(entry.range.start),
5567 height: message_height,
5568 render: diagnostic_block_renderer(diagnostic, true),
5569 disposition: BlockDisposition::Below,
5570 }
5571 }),
5572 cx,
5573 )
5574 .into_iter()
5575 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5576 .collect();
5577
5578 Some(ActiveDiagnosticGroup {
5579 primary_range,
5580 primary_message,
5581 blocks,
5582 is_valid: true,
5583 })
5584 });
5585 self.active_diagnostics.is_some()
5586 }
5587
5588 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5589 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5590 self.display_map.update(cx, |display_map, cx| {
5591 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5592 });
5593 cx.notify();
5594 }
5595 }
5596
5597 pub fn set_selections_from_remote(
5598 &mut self,
5599 selections: Vec<Selection<Anchor>>,
5600 pending_selection: Option<Selection<Anchor>>,
5601 cx: &mut ViewContext<Self>,
5602 ) {
5603 let old_cursor_position = self.selections.newest_anchor().head();
5604 self.selections.change_with(cx, |s| {
5605 s.select_anchors(selections);
5606 if let Some(pending_selection) = pending_selection {
5607 s.set_pending(pending_selection, SelectMode::Character);
5608 } else {
5609 s.clear_pending();
5610 }
5611 });
5612 self.selections_did_change(false, &old_cursor_position, cx);
5613 }
5614
5615 fn push_to_selection_history(&mut self) {
5616 self.selection_history.push(SelectionHistoryEntry {
5617 selections: self.selections.disjoint_anchors(),
5618 select_next_state: self.select_next_state.clone(),
5619 add_selections_state: self.add_selections_state.clone(),
5620 });
5621 }
5622
5623 pub fn transact(
5624 &mut self,
5625 cx: &mut ViewContext<Self>,
5626 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5627 ) -> Option<TransactionId> {
5628 self.start_transaction_at(Instant::now(), cx);
5629 update(self, cx);
5630 self.end_transaction_at(Instant::now(), cx)
5631 }
5632
5633 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5634 self.end_selection(cx);
5635 if let Some(tx_id) = self
5636 .buffer
5637 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5638 {
5639 self.selection_history
5640 .insert_transaction(tx_id, self.selections.disjoint_anchors());
5641 }
5642 }
5643
5644 fn end_transaction_at(
5645 &mut self,
5646 now: Instant,
5647 cx: &mut ViewContext<Self>,
5648 ) -> Option<TransactionId> {
5649 if let Some(tx_id) = self
5650 .buffer
5651 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5652 {
5653 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5654 *end_selections = Some(self.selections.disjoint_anchors());
5655 } else {
5656 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5657 }
5658
5659 cx.emit(Event::Edited);
5660 Some(tx_id)
5661 } else {
5662 None
5663 }
5664 }
5665
5666 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5667 let mut fold_ranges = Vec::new();
5668
5669 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5670 let selections = self.selections.all::<Point>(cx);
5671 for selection in selections {
5672 let range = selection.display_range(&display_map).sorted();
5673 let buffer_start_row = range.start.to_point(&display_map).row;
5674
5675 for row in (0..=range.end.row()).rev() {
5676 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5677 let fold_range = self.foldable_range_for_line(&display_map, row);
5678 if fold_range.end.row >= buffer_start_row {
5679 fold_ranges.push(fold_range);
5680 if row <= range.start.row() {
5681 break;
5682 }
5683 }
5684 }
5685 }
5686 }
5687
5688 self.fold_ranges(fold_ranges, cx);
5689 }
5690
5691 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5692 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5693 let buffer = &display_map.buffer_snapshot;
5694 let selections = self.selections.all::<Point>(cx);
5695 let ranges = selections
5696 .iter()
5697 .map(|s| {
5698 let range = s.display_range(&display_map).sorted();
5699 let mut start = range.start.to_point(&display_map);
5700 let mut end = range.end.to_point(&display_map);
5701 start.column = 0;
5702 end.column = buffer.line_len(end.row);
5703 start..end
5704 })
5705 .collect::<Vec<_>>();
5706 self.unfold_ranges(ranges, true, cx);
5707 }
5708
5709 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5710 let max_point = display_map.max_point();
5711 if display_row >= max_point.row() {
5712 false
5713 } else {
5714 let (start_indent, is_blank) = display_map.line_indent(display_row);
5715 if is_blank {
5716 false
5717 } else {
5718 for display_row in display_row + 1..=max_point.row() {
5719 let (indent, is_blank) = display_map.line_indent(display_row);
5720 if !is_blank {
5721 return indent > start_indent;
5722 }
5723 }
5724 false
5725 }
5726 }
5727 }
5728
5729 fn foldable_range_for_line(
5730 &self,
5731 display_map: &DisplaySnapshot,
5732 start_row: u32,
5733 ) -> Range<Point> {
5734 let max_point = display_map.max_point();
5735
5736 let (start_indent, _) = display_map.line_indent(start_row);
5737 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5738 let mut end = None;
5739 for row in start_row + 1..=max_point.row() {
5740 let (indent, is_blank) = display_map.line_indent(row);
5741 if !is_blank && indent <= start_indent {
5742 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5743 break;
5744 }
5745 }
5746
5747 let end = end.unwrap_or(max_point);
5748 start.to_point(display_map)..end.to_point(display_map)
5749 }
5750
5751 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5752 let selections = self.selections.all::<Point>(cx);
5753 let ranges = selections.into_iter().map(|s| s.start..s.end);
5754 self.fold_ranges(ranges, cx);
5755 }
5756
5757 pub fn fold_ranges<T: ToOffset>(
5758 &mut self,
5759 ranges: impl IntoIterator<Item = Range<T>>,
5760 cx: &mut ViewContext<Self>,
5761 ) {
5762 let mut ranges = ranges.into_iter().peekable();
5763 if ranges.peek().is_some() {
5764 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5765 self.request_autoscroll(Autoscroll::fit(), cx);
5766 cx.notify();
5767 }
5768 }
5769
5770 pub fn unfold_ranges<T: ToOffset>(
5771 &mut self,
5772 ranges: impl IntoIterator<Item = Range<T>>,
5773 inclusive: bool,
5774 cx: &mut ViewContext<Self>,
5775 ) {
5776 let mut ranges = ranges.into_iter().peekable();
5777 if ranges.peek().is_some() {
5778 self.display_map
5779 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5780 self.request_autoscroll(Autoscroll::fit(), cx);
5781 cx.notify();
5782 }
5783 }
5784
5785 pub fn insert_blocks(
5786 &mut self,
5787 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5788 cx: &mut ViewContext<Self>,
5789 ) -> Vec<BlockId> {
5790 let blocks = self
5791 .display_map
5792 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5793 self.request_autoscroll(Autoscroll::fit(), cx);
5794 blocks
5795 }
5796
5797 pub fn replace_blocks(
5798 &mut self,
5799 blocks: HashMap<BlockId, RenderBlock>,
5800 cx: &mut ViewContext<Self>,
5801 ) {
5802 self.display_map
5803 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5804 self.request_autoscroll(Autoscroll::fit(), cx);
5805 }
5806
5807 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5808 self.display_map.update(cx, |display_map, cx| {
5809 display_map.remove_blocks(block_ids, cx)
5810 });
5811 }
5812
5813 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5814 self.display_map
5815 .update(cx, |map, cx| map.snapshot(cx))
5816 .longest_row()
5817 }
5818
5819 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5820 self.display_map
5821 .update(cx, |map, cx| map.snapshot(cx))
5822 .max_point()
5823 }
5824
5825 pub fn text(&self, cx: &AppContext) -> String {
5826 self.buffer.read(cx).read(cx).text()
5827 }
5828
5829 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5830 self.transact(cx, |this, cx| {
5831 this.buffer
5832 .read(cx)
5833 .as_singleton()
5834 .expect("you can only call set_text on editors for singleton buffers")
5835 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5836 });
5837 }
5838
5839 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5840 self.display_map
5841 .update(cx, |map, cx| map.snapshot(cx))
5842 .text()
5843 }
5844
5845 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5846 let language_name = self
5847 .buffer
5848 .read(cx)
5849 .as_singleton()
5850 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5851 .map(|l| l.name());
5852
5853 let settings = cx.global::<Settings>();
5854 let mode = self
5855 .soft_wrap_mode_override
5856 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5857 match mode {
5858 settings::SoftWrap::None => SoftWrap::None,
5859 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5860 settings::SoftWrap::PreferredLineLength => {
5861 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5862 }
5863 }
5864 }
5865
5866 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5867 self.soft_wrap_mode_override = Some(mode);
5868 cx.notify();
5869 }
5870
5871 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5872 self.display_map
5873 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5874 }
5875
5876 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, cx: &mut ViewContext<Self>) {
5877 if self.soft_wrap_mode_override.is_some() {
5878 self.soft_wrap_mode_override.take();
5879 } else {
5880 let soft_wrap = match self.soft_wrap_mode(cx) {
5881 SoftWrap::None => settings::SoftWrap::EditorWidth,
5882 SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
5883 };
5884 self.soft_wrap_mode_override = Some(soft_wrap);
5885 }
5886 cx.notify();
5887 }
5888
5889 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5890 self.highlighted_rows = rows;
5891 }
5892
5893 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5894 self.highlighted_rows.clone()
5895 }
5896
5897 pub fn highlight_background<T: 'static>(
5898 &mut self,
5899 ranges: Vec<Range<Anchor>>,
5900 color_fetcher: fn(&Theme) -> Color,
5901 cx: &mut ViewContext<Self>,
5902 ) {
5903 self.background_highlights
5904 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5905 cx.notify();
5906 }
5907
5908 #[allow(clippy::type_complexity)]
5909 pub fn clear_background_highlights<T: 'static>(
5910 &mut self,
5911 cx: &mut ViewContext<Self>,
5912 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5913 let highlights = self.background_highlights.remove(&TypeId::of::<T>());
5914 if highlights.is_some() {
5915 cx.notify();
5916 }
5917 highlights
5918 }
5919
5920 #[cfg(feature = "test-support")]
5921 pub fn all_background_highlights(
5922 &mut self,
5923 cx: &mut ViewContext<Self>,
5924 ) -> Vec<(Range<DisplayPoint>, Color)> {
5925 let snapshot = self.snapshot(cx);
5926 let buffer = &snapshot.buffer_snapshot;
5927 let start = buffer.anchor_before(0);
5928 let end = buffer.anchor_after(buffer.len());
5929 let theme = cx.global::<Settings>().theme.as_ref();
5930 self.background_highlights_in_range(start..end, &snapshot, theme)
5931 }
5932
5933 fn document_highlights_for_position<'a>(
5934 &'a self,
5935 position: Anchor,
5936 buffer: &'a MultiBufferSnapshot,
5937 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5938 let read_highlights = self
5939 .background_highlights
5940 .get(&TypeId::of::<DocumentHighlightRead>())
5941 .map(|h| &h.1);
5942 let write_highlights = self
5943 .background_highlights
5944 .get(&TypeId::of::<DocumentHighlightWrite>())
5945 .map(|h| &h.1);
5946 let left_position = position.bias_left(buffer);
5947 let right_position = position.bias_right(buffer);
5948 read_highlights
5949 .into_iter()
5950 .chain(write_highlights)
5951 .flat_map(move |ranges| {
5952 let start_ix = match ranges.binary_search_by(|probe| {
5953 let cmp = probe.end.cmp(&left_position, buffer);
5954 if cmp.is_ge() {
5955 Ordering::Greater
5956 } else {
5957 Ordering::Less
5958 }
5959 }) {
5960 Ok(i) | Err(i) => i,
5961 };
5962
5963 let right_position = right_position.clone();
5964 ranges[start_ix..]
5965 .iter()
5966 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
5967 })
5968 }
5969
5970 pub fn background_highlights_in_range(
5971 &self,
5972 search_range: Range<Anchor>,
5973 display_snapshot: &DisplaySnapshot,
5974 theme: &Theme,
5975 ) -> Vec<(Range<DisplayPoint>, Color)> {
5976 let mut results = Vec::new();
5977 let buffer = &display_snapshot.buffer_snapshot;
5978 for (color_fetcher, ranges) in self.background_highlights.values() {
5979 let color = color_fetcher(theme);
5980 let start_ix = match ranges.binary_search_by(|probe| {
5981 let cmp = probe.end.cmp(&search_range.start, buffer);
5982 if cmp.is_gt() {
5983 Ordering::Greater
5984 } else {
5985 Ordering::Less
5986 }
5987 }) {
5988 Ok(i) | Err(i) => i,
5989 };
5990 for range in &ranges[start_ix..] {
5991 if range.start.cmp(&search_range.end, buffer).is_ge() {
5992 break;
5993 }
5994 let start = range
5995 .start
5996 .to_point(buffer)
5997 .to_display_point(display_snapshot);
5998 let end = range
5999 .end
6000 .to_point(buffer)
6001 .to_display_point(display_snapshot);
6002 results.push((start..end, color))
6003 }
6004 }
6005 results
6006 }
6007
6008 pub fn highlight_text<T: 'static>(
6009 &mut self,
6010 ranges: Vec<Range<Anchor>>,
6011 style: HighlightStyle,
6012 cx: &mut ViewContext<Self>,
6013 ) {
6014 self.display_map.update(cx, |map, _| {
6015 map.highlight_text(TypeId::of::<T>(), ranges, style)
6016 });
6017 cx.notify();
6018 }
6019
6020 pub fn text_highlights<'a, T: 'static>(
6021 &'a self,
6022 cx: &'a AppContext,
6023 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
6024 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
6025 }
6026
6027 pub fn clear_text_highlights<T: 'static>(
6028 &mut self,
6029 cx: &mut ViewContext<Self>,
6030 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
6031 let highlights = self
6032 .display_map
6033 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
6034 if highlights.is_some() {
6035 cx.notify();
6036 }
6037 highlights
6038 }
6039
6040 pub fn show_local_cursors(&self, cx: &AppContext) -> bool {
6041 self.blink_manager.read(cx).visible() && self.focused
6042 }
6043
6044 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
6045 cx.notify();
6046 }
6047
6048 fn on_buffer_event(
6049 &mut self,
6050 _: ModelHandle<MultiBuffer>,
6051 event: &multi_buffer::Event,
6052 cx: &mut ViewContext<Self>,
6053 ) {
6054 match event {
6055 multi_buffer::Event::Edited => {
6056 self.refresh_active_diagnostics(cx);
6057 self.refresh_code_actions(cx);
6058 cx.emit(Event::BufferEdited);
6059 }
6060 multi_buffer::Event::ExcerptsAdded {
6061 buffer,
6062 predecessor,
6063 excerpts,
6064 } => cx.emit(Event::ExcerptsAdded {
6065 buffer: buffer.clone(),
6066 predecessor: *predecessor,
6067 excerpts: excerpts.clone(),
6068 }),
6069 multi_buffer::Event::ExcerptsRemoved { ids } => {
6070 cx.emit(Event::ExcerptsRemoved { ids: ids.clone() })
6071 }
6072 multi_buffer::Event::Reparsed => cx.emit(Event::Reparsed),
6073 multi_buffer::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
6074 multi_buffer::Event::Saved => cx.emit(Event::Saved),
6075 multi_buffer::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
6076 multi_buffer::Event::Reloaded => cx.emit(Event::TitleChanged),
6077 multi_buffer::Event::Closed => cx.emit(Event::Closed),
6078 multi_buffer::Event::DiagnosticsUpdated => {
6079 self.refresh_active_diagnostics(cx);
6080 }
6081 }
6082 }
6083
6084 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
6085 cx.notify();
6086 }
6087
6088 pub fn set_searchable(&mut self, searchable: bool) {
6089 self.searchable = searchable;
6090 }
6091
6092 pub fn searchable(&self) -> bool {
6093 self.searchable
6094 }
6095
6096 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
6097 let active_item = workspace.active_item(cx);
6098 let editor_handle = if let Some(editor) = active_item
6099 .as_ref()
6100 .and_then(|item| item.act_as::<Self>(cx))
6101 {
6102 editor
6103 } else {
6104 cx.propagate_action();
6105 return;
6106 };
6107
6108 let editor = editor_handle.read(cx);
6109 let buffer = editor.buffer.read(cx);
6110 if buffer.is_singleton() {
6111 cx.propagate_action();
6112 return;
6113 }
6114
6115 let mut new_selections_by_buffer = HashMap::default();
6116 for selection in editor.selections.all::<usize>(cx) {
6117 for (buffer, mut range) in
6118 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
6119 {
6120 if selection.reversed {
6121 mem::swap(&mut range.start, &mut range.end);
6122 }
6123 new_selections_by_buffer
6124 .entry(buffer)
6125 .or_insert(Vec::new())
6126 .push(range)
6127 }
6128 }
6129
6130 editor_handle.update(cx, |editor, cx| {
6131 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
6132 });
6133 let pane = workspace.active_pane().clone();
6134 pane.update(cx, |pane, _| pane.disable_history());
6135
6136 // We defer the pane interaction because we ourselves are a workspace item
6137 // and activating a new item causes the pane to call a method on us reentrantly,
6138 // which panics if we're on the stack.
6139 cx.defer(move |workspace, cx| {
6140 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
6141 let editor = workspace.open_project_item::<Self>(buffer, cx);
6142 editor.update(cx, |editor, cx| {
6143 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6144 s.select_ranges(ranges);
6145 });
6146 });
6147 }
6148
6149 pane.update(cx, |pane, _| pane.enable_history());
6150 });
6151 }
6152
6153 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
6154 let editor = workspace.open_path(action.path.clone(), None, true, cx);
6155 let position = action.position;
6156 let anchor = action.anchor;
6157 cx.spawn_weak(|_, mut cx| async move {
6158 let editor = editor.await.log_err()?.downcast::<Editor>()?;
6159 editor.update(&mut cx, |editor, cx| {
6160 let buffer = editor.buffer().read(cx).as_singleton()?;
6161 let buffer = buffer.read(cx);
6162 let cursor = if buffer.can_resolve(&anchor) {
6163 language::ToPoint::to_point(&anchor, buffer)
6164 } else {
6165 buffer.clip_point(position, Bias::Left)
6166 };
6167
6168 let nav_history = editor.nav_history.take();
6169 editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
6170 s.select_ranges([cursor..cursor]);
6171 });
6172 editor.nav_history = nav_history;
6173
6174 Some(())
6175 })?;
6176 Some(())
6177 })
6178 .detach()
6179 }
6180
6181 fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {
6182 let snapshot = self.buffer.read(cx).read(cx);
6183 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
6184 Some(
6185 ranges
6186 .iter()
6187 .map(move |range| {
6188 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
6189 })
6190 .collect(),
6191 )
6192 }
6193
6194 fn selection_replacement_ranges(
6195 &self,
6196 range: Range<OffsetUtf16>,
6197 cx: &AppContext,
6198 ) -> Vec<Range<OffsetUtf16>> {
6199 let selections = self.selections.all::<OffsetUtf16>(cx);
6200 let newest_selection = selections
6201 .iter()
6202 .max_by_key(|selection| selection.id)
6203 .unwrap();
6204 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
6205 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
6206 let snapshot = self.buffer.read(cx).read(cx);
6207 selections
6208 .into_iter()
6209 .map(|mut selection| {
6210 selection.start.0 =
6211 (selection.start.0 as isize).saturating_add(start_delta) as usize;
6212 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
6213 snapshot.clip_offset_utf16(selection.start, Bias::Left)
6214 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
6215 })
6216 .collect()
6217 }
6218
6219 fn report_event(&self, name: &str, cx: &AppContext) {
6220 if let Some((project, file)) = self.project.as_ref().zip(
6221 self.buffer
6222 .read(cx)
6223 .as_singleton()
6224 .and_then(|b| b.read(cx).file()),
6225 ) {
6226 let extension = Path::new(file.file_name(cx))
6227 .extension()
6228 .and_then(|e| e.to_str());
6229 project.read(cx).client().report_event(
6230 name,
6231 json!({ "File Extension": extension }),
6232 cx.global::<Settings>().telemetry(),
6233 );
6234 }
6235 }
6236}
6237
6238impl EditorSnapshot {
6239 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
6240 self.display_snapshot.buffer_snapshot.language_at(position)
6241 }
6242
6243 pub fn is_focused(&self) -> bool {
6244 self.is_focused
6245 }
6246
6247 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
6248 self.placeholder_text.as_ref()
6249 }
6250
6251 pub fn scroll_position(&self) -> Vector2F {
6252 self.scroll_anchor.scroll_position(&self.display_snapshot)
6253 }
6254}
6255
6256impl Deref for EditorSnapshot {
6257 type Target = DisplaySnapshot;
6258
6259 fn deref(&self) -> &Self::Target {
6260 &self.display_snapshot
6261 }
6262}
6263
6264#[derive(Clone, Debug, PartialEq, Eq)]
6265pub enum Event {
6266 InputIgnored {
6267 text: Arc<str>,
6268 },
6269 ExcerptsAdded {
6270 buffer: ModelHandle<Buffer>,
6271 predecessor: ExcerptId,
6272 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
6273 },
6274 ExcerptsRemoved {
6275 ids: Vec<ExcerptId>,
6276 },
6277 BufferEdited,
6278 Edited,
6279 Reparsed,
6280 Blurred,
6281 DirtyChanged,
6282 Saved,
6283 TitleChanged,
6284 SelectionsChanged {
6285 local: bool,
6286 },
6287 ScrollPositionChanged {
6288 local: bool,
6289 },
6290 Closed,
6291}
6292
6293pub struct EditorFocused(pub ViewHandle<Editor>);
6294pub struct EditorBlurred(pub ViewHandle<Editor>);
6295pub struct EditorReleased(pub WeakViewHandle<Editor>);
6296
6297impl Entity for Editor {
6298 type Event = Event;
6299
6300 fn release(&mut self, cx: &mut MutableAppContext) {
6301 cx.emit_global(EditorReleased(self.handle.clone()));
6302 }
6303}
6304
6305impl View for Editor {
6306 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
6307 let style = self.style(cx);
6308 let font_changed = self.display_map.update(cx, |map, cx| {
6309 map.set_font(style.text.font_id, style.text.font_size, cx)
6310 });
6311
6312 if font_changed {
6313 let handle = self.handle.clone();
6314 cx.defer(move |cx| {
6315 if let Some(editor) = handle.upgrade(cx) {
6316 editor.update(cx, |editor, cx| {
6317 hide_hover(editor, &HideHover, cx);
6318 hide_link_definition(editor, cx);
6319 })
6320 }
6321 });
6322 }
6323
6324 Stack::new()
6325 .with_child(EditorElement::new(self.handle.clone(), style.clone()).boxed())
6326 .with_child(ChildView::new(&self.mouse_context_menu, cx).boxed())
6327 .boxed()
6328 }
6329
6330 fn ui_name() -> &'static str {
6331 "Editor"
6332 }
6333
6334 fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6335 if cx.is_self_focused() {
6336 let focused_event = EditorFocused(cx.handle());
6337 cx.emit_global(focused_event);
6338 }
6339 if let Some(rename) = self.pending_rename.as_ref() {
6340 cx.focus(&rename.editor);
6341 } else {
6342 if !self.focused {
6343 self.blink_manager.update(cx, BlinkManager::enable);
6344 }
6345 self.focused = true;
6346 self.buffer.update(cx, |buffer, cx| {
6347 buffer.finalize_last_transaction(cx);
6348 if self.leader_replica_id.is_none() {
6349 buffer.set_active_selections(
6350 &self.selections.disjoint_anchors(),
6351 self.selections.line_mode,
6352 self.cursor_shape,
6353 cx,
6354 );
6355 }
6356 });
6357 }
6358 }
6359
6360 fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
6361 let blurred_event = EditorBlurred(cx.handle());
6362 cx.emit_global(blurred_event);
6363 self.focused = false;
6364 self.blink_manager.update(cx, BlinkManager::disable);
6365 self.buffer
6366 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
6367 self.hide_context_menu(cx);
6368 hide_hover(self, &HideHover, cx);
6369 cx.emit(Event::Blurred);
6370 cx.notify();
6371 }
6372
6373 fn modifiers_changed(
6374 &mut self,
6375 event: &gpui::ModifiersChangedEvent,
6376 cx: &mut ViewContext<Self>,
6377 ) -> bool {
6378 let pending_selection = self.has_pending_selection();
6379
6380 if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() {
6381 if event.cmd && !pending_selection {
6382 let snapshot = self.snapshot(cx);
6383 let kind = if event.shift {
6384 LinkDefinitionKind::Type
6385 } else {
6386 LinkDefinitionKind::Symbol
6387 };
6388
6389 show_link_definition(kind, self, point, snapshot, cx);
6390 return false;
6391 }
6392 }
6393
6394 {
6395 if self.link_go_to_definition_state.symbol_range.is_some()
6396 || !self.link_go_to_definition_state.definitions.is_empty()
6397 {
6398 self.link_go_to_definition_state.symbol_range.take();
6399 self.link_go_to_definition_state.definitions.clear();
6400 cx.notify();
6401 }
6402
6403 self.link_go_to_definition_state.task = None;
6404
6405 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
6406 }
6407
6408 false
6409 }
6410
6411 fn keymap_context(&self, _: &AppContext) -> KeymapContext {
6412 let mut context = Self::default_keymap_context();
6413 let mode = match self.mode {
6414 EditorMode::SingleLine => "single_line",
6415 EditorMode::AutoHeight { .. } => "auto_height",
6416 EditorMode::Full => "full",
6417 };
6418 context.map.insert("mode".into(), mode.into());
6419 if self.pending_rename.is_some() {
6420 context.set.insert("renaming".into());
6421 }
6422 match self.context_menu.as_ref() {
6423 Some(ContextMenu::Completions(_)) => {
6424 context.set.insert("showing_completions".into());
6425 }
6426 Some(ContextMenu::CodeActions(_)) => {
6427 context.set.insert("showing_code_actions".into());
6428 }
6429 None => {}
6430 }
6431
6432 for layer in self.keymap_context_layers.values() {
6433 context.extend(layer);
6434 }
6435
6436 context
6437 }
6438
6439 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
6440 Some(
6441 self.buffer
6442 .read(cx)
6443 .read(cx)
6444 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
6445 .collect(),
6446 )
6447 }
6448
6449 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6450 // Prevent the IME menu from appearing when holding down an alphabetic key
6451 // while input is disabled.
6452 if !self.input_enabled {
6453 return None;
6454 }
6455
6456 let range = self.selections.newest::<OffsetUtf16>(cx).range();
6457 Some(range.start.0..range.end.0)
6458 }
6459
6460 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
6461 let snapshot = self.buffer.read(cx).read(cx);
6462 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
6463 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
6464 }
6465
6466 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
6467 self.clear_text_highlights::<InputComposition>(cx);
6468 self.ime_transaction.take();
6469 }
6470
6471 fn replace_text_in_range(
6472 &mut self,
6473 range_utf16: Option<Range<usize>>,
6474 text: &str,
6475 cx: &mut ViewContext<Self>,
6476 ) {
6477 self.transact(cx, |this, cx| {
6478 if this.input_enabled {
6479 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
6480 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6481 Some(this.selection_replacement_ranges(range_utf16, cx))
6482 } else {
6483 this.marked_text_ranges(cx)
6484 };
6485
6486 if let Some(new_selected_ranges) = new_selected_ranges {
6487 this.change_selections(None, cx, |selections| {
6488 selections.select_ranges(new_selected_ranges)
6489 });
6490 }
6491 }
6492
6493 this.handle_input(text, cx);
6494 });
6495
6496 if !self.input_enabled {
6497 return;
6498 }
6499
6500 if let Some(transaction) = self.ime_transaction {
6501 self.buffer.update(cx, |buffer, cx| {
6502 buffer.group_until_transaction(transaction, cx);
6503 });
6504 }
6505
6506 self.unmark_text(cx);
6507 }
6508
6509 fn replace_and_mark_text_in_range(
6510 &mut self,
6511 range_utf16: Option<Range<usize>>,
6512 text: &str,
6513 new_selected_range_utf16: Option<Range<usize>>,
6514 cx: &mut ViewContext<Self>,
6515 ) {
6516 if !self.input_enabled {
6517 return;
6518 }
6519
6520 let transaction = self.transact(cx, |this, cx| {
6521 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
6522 let snapshot = this.buffer.read(cx).read(cx);
6523 if let Some(relative_range_utf16) = range_utf16.as_ref() {
6524 for marked_range in &mut marked_ranges {
6525 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
6526 marked_range.start.0 += relative_range_utf16.start;
6527 marked_range.start =
6528 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
6529 marked_range.end =
6530 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
6531 }
6532 }
6533 Some(marked_ranges)
6534 } else if let Some(range_utf16) = range_utf16 {
6535 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
6536 Some(this.selection_replacement_ranges(range_utf16, cx))
6537 } else {
6538 None
6539 };
6540
6541 if let Some(ranges) = ranges_to_replace {
6542 this.change_selections(None, cx, |s| s.select_ranges(ranges));
6543 }
6544
6545 let marked_ranges = {
6546 let snapshot = this.buffer.read(cx).read(cx);
6547 this.selections
6548 .disjoint_anchors()
6549 .iter()
6550 .map(|selection| {
6551 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
6552 })
6553 .collect::<Vec<_>>()
6554 };
6555
6556 if text.is_empty() {
6557 this.unmark_text(cx);
6558 } else {
6559 this.highlight_text::<InputComposition>(
6560 marked_ranges.clone(),
6561 this.style(cx).composition_mark,
6562 cx,
6563 );
6564 }
6565
6566 this.handle_input(text, cx);
6567
6568 if let Some(new_selected_range) = new_selected_range_utf16 {
6569 let snapshot = this.buffer.read(cx).read(cx);
6570 let new_selected_ranges = marked_ranges
6571 .into_iter()
6572 .map(|marked_range| {
6573 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
6574 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
6575 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
6576 snapshot.clip_offset_utf16(new_start, Bias::Left)
6577 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
6578 })
6579 .collect::<Vec<_>>();
6580
6581 drop(snapshot);
6582 this.change_selections(None, cx, |selections| {
6583 selections.select_ranges(new_selected_ranges)
6584 });
6585 }
6586 });
6587
6588 self.ime_transaction = self.ime_transaction.or(transaction);
6589 if let Some(transaction) = self.ime_transaction {
6590 self.buffer.update(cx, |buffer, cx| {
6591 buffer.group_until_transaction(transaction, cx);
6592 });
6593 }
6594
6595 if self.text_highlights::<InputComposition>(cx).is_none() {
6596 self.ime_transaction.take();
6597 }
6598 }
6599}
6600
6601fn build_style(
6602 settings: &Settings,
6603 get_field_editor_theme: Option<&GetFieldEditorTheme>,
6604 override_text_style: Option<&OverrideTextStyle>,
6605 cx: &AppContext,
6606) -> EditorStyle {
6607 let font_cache = cx.font_cache();
6608
6609 let mut theme = settings.theme.editor.clone();
6610 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
6611 let field_editor_theme = get_field_editor_theme(&settings.theme);
6612 theme.text_color = field_editor_theme.text.color;
6613 theme.selection = field_editor_theme.selection;
6614 theme.background = field_editor_theme
6615 .container
6616 .background_color
6617 .unwrap_or_default();
6618 EditorStyle {
6619 text: field_editor_theme.text,
6620 placeholder_text: field_editor_theme.placeholder_text,
6621 theme,
6622 }
6623 } else {
6624 let font_family_id = settings.buffer_font_family;
6625 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
6626 let font_properties = Default::default();
6627 let font_id = font_cache
6628 .select_font(font_family_id, &font_properties)
6629 .unwrap();
6630 let font_size = settings.buffer_font_size;
6631 EditorStyle {
6632 text: TextStyle {
6633 color: settings.theme.editor.text_color,
6634 font_family_name,
6635 font_family_id,
6636 font_id,
6637 font_size,
6638 font_properties,
6639 underline: Default::default(),
6640 },
6641 placeholder_text: None,
6642 theme,
6643 }
6644 };
6645
6646 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
6647 if let Some(highlighted) = style
6648 .text
6649 .clone()
6650 .highlight(highlight_style, font_cache)
6651 .log_err()
6652 {
6653 style.text = highlighted;
6654 }
6655 }
6656
6657 style
6658}
6659
6660trait SelectionExt {
6661 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
6662 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
6663 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
6664 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
6665 -> Range<u32>;
6666}
6667
6668impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
6669 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
6670 let start = self.start.to_point(buffer);
6671 let end = self.end.to_point(buffer);
6672 if self.reversed {
6673 end..start
6674 } else {
6675 start..end
6676 }
6677 }
6678
6679 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
6680 let start = self.start.to_offset(buffer);
6681 let end = self.end.to_offset(buffer);
6682 if self.reversed {
6683 end..start
6684 } else {
6685 start..end
6686 }
6687 }
6688
6689 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
6690 let start = self
6691 .start
6692 .to_point(&map.buffer_snapshot)
6693 .to_display_point(map);
6694 let end = self
6695 .end
6696 .to_point(&map.buffer_snapshot)
6697 .to_display_point(map);
6698 if self.reversed {
6699 end..start
6700 } else {
6701 start..end
6702 }
6703 }
6704
6705 fn spanned_rows(
6706 &self,
6707 include_end_if_at_line_start: bool,
6708 map: &DisplaySnapshot,
6709 ) -> Range<u32> {
6710 let start = self.start.to_point(&map.buffer_snapshot);
6711 let mut end = self.end.to_point(&map.buffer_snapshot);
6712 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
6713 end.row -= 1;
6714 }
6715
6716 let buffer_start = map.prev_line_boundary(start).0;
6717 let buffer_end = map.next_line_boundary(end).0;
6718 buffer_start.row..buffer_end.row + 1
6719 }
6720}
6721
6722impl<T: InvalidationRegion> InvalidationStack<T> {
6723 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
6724 where
6725 S: Clone + ToOffset,
6726 {
6727 while let Some(region) = self.last() {
6728 let all_selections_inside_invalidation_ranges =
6729 if selections.len() == region.ranges().len() {
6730 selections
6731 .iter()
6732 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
6733 .all(|(selection, invalidation_range)| {
6734 let head = selection.head().to_offset(buffer);
6735 invalidation_range.start <= head && invalidation_range.end >= head
6736 })
6737 } else {
6738 false
6739 };
6740
6741 if all_selections_inside_invalidation_ranges {
6742 break;
6743 } else {
6744 self.pop();
6745 }
6746 }
6747 }
6748}
6749
6750impl<T> Default for InvalidationStack<T> {
6751 fn default() -> Self {
6752 Self(Default::default())
6753 }
6754}
6755
6756impl<T> Deref for InvalidationStack<T> {
6757 type Target = Vec<T>;
6758
6759 fn deref(&self) -> &Self::Target {
6760 &self.0
6761 }
6762}
6763
6764impl<T> DerefMut for InvalidationStack<T> {
6765 fn deref_mut(&mut self) -> &mut Self::Target {
6766 &mut self.0
6767 }
6768}
6769
6770impl InvalidationRegion for SnippetState {
6771 fn ranges(&self) -> &[Range<Anchor>] {
6772 &self.ranges[self.active_index]
6773 }
6774}
6775
6776impl Deref for EditorStyle {
6777 type Target = theme::Editor;
6778
6779 fn deref(&self) -> &Self::Target {
6780 &self.theme
6781 }
6782}
6783
6784pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6785 let mut highlighted_lines = Vec::new();
6786 for line in diagnostic.message.lines() {
6787 highlighted_lines.push(highlight_diagnostic_message(line));
6788 }
6789
6790 Arc::new(move |cx: &mut BlockContext| {
6791 let settings = cx.global::<Settings>();
6792 let theme = &settings.theme.editor;
6793 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6794 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6795 Flex::column()
6796 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6797 Label::new(
6798 line.clone(),
6799 style.message.clone().with_font_size(font_size),
6800 )
6801 .with_highlights(highlights.clone())
6802 .contained()
6803 .with_margin_left(cx.anchor_x)
6804 .boxed()
6805 }))
6806 .aligned()
6807 .left()
6808 .boxed()
6809 })
6810}
6811
6812pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6813 let mut message_without_backticks = String::new();
6814 let mut prev_offset = 0;
6815 let mut inside_block = false;
6816 let mut highlights = Vec::new();
6817 for (match_ix, (offset, _)) in message
6818 .match_indices('`')
6819 .chain([(message.len(), "")])
6820 .enumerate()
6821 {
6822 message_without_backticks.push_str(&message[prev_offset..offset]);
6823 if inside_block {
6824 highlights.extend(prev_offset - match_ix..offset - match_ix);
6825 }
6826
6827 inside_block = !inside_block;
6828 prev_offset = offset + 1;
6829 }
6830
6831 (message_without_backticks, highlights)
6832}
6833
6834pub fn diagnostic_style(
6835 severity: DiagnosticSeverity,
6836 valid: bool,
6837 theme: &theme::Editor,
6838) -> DiagnosticStyle {
6839 match (severity, valid) {
6840 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6841 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6842 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6843 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6844 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6845 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6846 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6847 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6848 _ => theme.invalid_hint_diagnostic.clone(),
6849 }
6850}
6851
6852pub fn combine_syntax_and_fuzzy_match_highlights(
6853 text: &str,
6854 default_style: HighlightStyle,
6855 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6856 match_indices: &[usize],
6857) -> Vec<(Range<usize>, HighlightStyle)> {
6858 let mut result = Vec::new();
6859 let mut match_indices = match_indices.iter().copied().peekable();
6860
6861 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6862 {
6863 syntax_highlight.weight = None;
6864
6865 // Add highlights for any fuzzy match characters before the next
6866 // syntax highlight range.
6867 while let Some(&match_index) = match_indices.peek() {
6868 if match_index >= range.start {
6869 break;
6870 }
6871 match_indices.next();
6872 let end_index = char_ix_after(match_index, text);
6873 let mut match_style = default_style;
6874 match_style.weight = Some(fonts::Weight::BOLD);
6875 result.push((match_index..end_index, match_style));
6876 }
6877
6878 if range.start == usize::MAX {
6879 break;
6880 }
6881
6882 // Add highlights for any fuzzy match characters within the
6883 // syntax highlight range.
6884 let mut offset = range.start;
6885 while let Some(&match_index) = match_indices.peek() {
6886 if match_index >= range.end {
6887 break;
6888 }
6889
6890 match_indices.next();
6891 if match_index > offset {
6892 result.push((offset..match_index, syntax_highlight));
6893 }
6894
6895 let mut end_index = char_ix_after(match_index, text);
6896 while let Some(&next_match_index) = match_indices.peek() {
6897 if next_match_index == end_index && next_match_index < range.end {
6898 end_index = char_ix_after(next_match_index, text);
6899 match_indices.next();
6900 } else {
6901 break;
6902 }
6903 }
6904
6905 let mut match_style = syntax_highlight;
6906 match_style.weight = Some(fonts::Weight::BOLD);
6907 result.push((match_index..end_index, match_style));
6908 offset = end_index;
6909 }
6910
6911 if offset < range.end {
6912 result.push((offset..range.end, syntax_highlight));
6913 }
6914 }
6915
6916 fn char_ix_after(ix: usize, text: &str) -> usize {
6917 ix + text[ix..].chars().next().unwrap().len_utf8()
6918 }
6919
6920 result
6921}
6922
6923pub fn styled_runs_for_code_label<'a>(
6924 label: &'a CodeLabel,
6925 syntax_theme: &'a theme::SyntaxTheme,
6926) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6927 let fade_out = HighlightStyle {
6928 fade_out: Some(0.35),
6929 ..Default::default()
6930 };
6931
6932 let mut prev_end = label.filter_range.end;
6933 label
6934 .runs
6935 .iter()
6936 .enumerate()
6937 .flat_map(move |(ix, (range, highlight_id))| {
6938 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6939 style
6940 } else {
6941 return Default::default();
6942 };
6943 let mut muted_style = style;
6944 muted_style.highlight(fade_out);
6945
6946 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6947 if range.start >= label.filter_range.end {
6948 if range.start > prev_end {
6949 runs.push((prev_end..range.start, fade_out));
6950 }
6951 runs.push((range.clone(), muted_style));
6952 } else if range.end <= label.filter_range.end {
6953 runs.push((range.clone(), style));
6954 } else {
6955 runs.push((range.start..label.filter_range.end, style));
6956 runs.push((label.filter_range.end..range.end, muted_style));
6957 }
6958 prev_end = cmp::max(prev_end, range.end);
6959
6960 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6961 runs.push((prev_end..label.text.len(), fade_out));
6962 }
6963
6964 runs
6965 })
6966}
6967
6968pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
6969 let mut index = 0;
6970 let mut codepoints = text.char_indices().peekable();
6971
6972 std::iter::from_fn(move || {
6973 let start_index = index;
6974 while let Some((new_index, codepoint)) = codepoints.next() {
6975 index = new_index + codepoint.len_utf8();
6976 let current_upper = codepoint.is_uppercase();
6977 let next_upper = codepoints
6978 .peek()
6979 .map(|(_, c)| c.is_uppercase())
6980 .unwrap_or(false);
6981
6982 if !current_upper && next_upper {
6983 return Some(&text[start_index..index]);
6984 }
6985 }
6986
6987 index = text.len();
6988 if start_index < text.len() {
6989 return Some(&text[start_index..]);
6990 }
6991 None
6992 })
6993 .flat_map(|word| word.split_inclusive('_'))
6994}
6995
6996trait RangeToAnchorExt {
6997 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
6998}
6999
7000impl<T: ToOffset> RangeToAnchorExt for Range<T> {
7001 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
7002 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
7003 }
7004}