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