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