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::{hide_link_definition, show_link_definition, LinkGoToDefinitionState};
68use log::error;
69use multi_buffer::ToOffsetUtf16;
70pub use multi_buffer::{
71 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
72 ToPoint,
73};
74use ordered_float::OrderedFloat;
75use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
76use rand::{seq::SliceRandom, thread_rng};
77use scroll::{
78 autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
79};
80use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
81use serde::{Deserialize, Serialize};
82use settings::SettingsStore;
83use smallvec::SmallVec;
84use snippet::Snippet;
85use std::{
86 any::TypeId,
87 borrow::Cow,
88 cmp::{self, Ordering, Reverse},
89 mem,
90 num::NonZeroU32,
91 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
92 path::Path,
93 sync::Arc,
94 time::{Duration, Instant},
95};
96pub use sum_tree::Bias;
97use text::Rope;
98use theme::{DiagnosticStyle, Theme, ThemeSettings};
99use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
100use workspace::{ItemNavHistory, ViewId, Workspace};
101
102use crate::git::diff_hunk_to_display;
103
104const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
105const MAX_LINE_LEN: usize = 1024;
106const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
107const MAX_SELECTION_HISTORY_LEN: usize = 1024;
108const COPILOT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
109
110pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
111
112#[derive(Clone, Deserialize, PartialEq, Default)]
113pub struct SelectNext {
114 #[serde(default)]
115 pub replace_newest: bool,
116}
117
118#[derive(Clone, Deserialize, PartialEq, Default)]
119pub struct SelectPrevious {
120 #[serde(default)]
121 pub replace_newest: bool,
122}
123
124#[derive(Clone, Deserialize, PartialEq)]
125pub struct SelectToBeginningOfLine {
126 #[serde(default)]
127 stop_at_soft_wraps: bool,
128}
129
130#[derive(Clone, Default, Deserialize, PartialEq)]
131pub struct MovePageUp {
132 #[serde(default)]
133 center_cursor: bool,
134}
135
136#[derive(Clone, Default, Deserialize, PartialEq)]
137pub struct MovePageDown {
138 #[serde(default)]
139 center_cursor: bool,
140}
141
142#[derive(Clone, Deserialize, PartialEq)]
143pub struct SelectToEndOfLine {
144 #[serde(default)]
145 stop_at_soft_wraps: bool,
146}
147
148#[derive(Clone, Deserialize, PartialEq)]
149pub struct ToggleCodeActions {
150 #[serde(default)]
151 pub deployed_from_indicator: bool,
152}
153
154#[derive(Clone, Default, Deserialize, PartialEq)]
155pub struct ConfirmCompletion {
156 #[serde(default)]
157 pub item_ix: Option<usize>,
158}
159
160#[derive(Clone, Default, Deserialize, PartialEq)]
161pub struct ConfirmCodeAction {
162 #[serde(default)]
163 pub item_ix: Option<usize>,
164}
165
166#[derive(Clone, Default, Deserialize, PartialEq)]
167pub struct ToggleComments {
168 #[serde(default)]
169 pub advance_downwards: bool,
170}
171
172#[derive(Clone, Default, Deserialize, PartialEq)]
173pub struct FoldAt {
174 pub buffer_row: u32,
175}
176
177#[derive(Clone, Default, Deserialize, PartialEq)]
178pub struct UnfoldAt {
179 pub buffer_row: u32,
180}
181
182#[derive(Clone, Default, Deserialize, PartialEq)]
183pub struct GutterHover {
184 pub hovered: bool,
185}
186
187#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
188pub enum InlayId {
189 Suggestion(usize),
190 Hint(usize),
191}
192
193impl InlayId {
194 fn id(&self) -> usize {
195 match self {
196 Self::Suggestion(id) => *id,
197 Self::Hint(id) => *id,
198 }
199 }
200}
201
202actions!(
203 editor,
204 [
205 Cancel,
206 Backspace,
207 Delete,
208 Newline,
209 NewlineAbove,
210 NewlineBelow,
211 GoToDiagnostic,
212 GoToPrevDiagnostic,
213 GoToHunk,
214 GoToPrevHunk,
215 Indent,
216 Outdent,
217 DeleteLine,
218 DeleteToPreviousWordStart,
219 DeleteToPreviousSubwordStart,
220 DeleteToNextWordEnd,
221 DeleteToNextSubwordEnd,
222 DeleteToBeginningOfLine,
223 DeleteToEndOfLine,
224 CutToEndOfLine,
225 DuplicateLine,
226 MoveLineUp,
227 MoveLineDown,
228 JoinLines,
229 SortLinesCaseSensitive,
230 SortLinesCaseInsensitive,
231 ReverseLines,
232 ShuffleLines,
233 ConvertToUpperCase,
234 ConvertToLowerCase,
235 ConvertToTitleCase,
236 ConvertToSnakeCase,
237 ConvertToKebabCase,
238 ConvertToUpperCamelCase,
239 ConvertToLowerCamelCase,
240 Transpose,
241 Cut,
242 Copy,
243 Paste,
244 Undo,
245 Redo,
246 MoveUp,
247 PageUp,
248 MoveDown,
249 PageDown,
250 MoveLeft,
251 MoveRight,
252 MoveToPreviousWordStart,
253 MoveToPreviousSubwordStart,
254 MoveToNextWordEnd,
255 MoveToNextSubwordEnd,
256 MoveToBeginningOfLine,
257 MoveToEndOfLine,
258 MoveToStartOfParagraph,
259 MoveToEndOfParagraph,
260 MoveToBeginning,
261 MoveToEnd,
262 SelectUp,
263 SelectDown,
264 SelectLeft,
265 SelectRight,
266 SelectToPreviousWordStart,
267 SelectToPreviousSubwordStart,
268 SelectToNextWordEnd,
269 SelectToNextSubwordEnd,
270 SelectToStartOfParagraph,
271 SelectToEndOfParagraph,
272 SelectToBeginning,
273 SelectToEnd,
274 SelectAll,
275 SelectLine,
276 SplitSelectionIntoLines,
277 AddSelectionAbove,
278 AddSelectionBelow,
279 Tab,
280 TabPrev,
281 ShowCharacterPalette,
282 SelectLargerSyntaxNode,
283 SelectSmallerSyntaxNode,
284 GoToDefinition,
285 GoToDefinitionSplit,
286 GoToTypeDefinition,
287 GoToTypeDefinitionSplit,
288 MoveToEnclosingBracket,
289 UndoSelection,
290 RedoSelection,
291 FindAllReferences,
292 Rename,
293 ConfirmRename,
294 Fold,
295 UnfoldLines,
296 FoldSelectedRanges,
297 ShowCompletions,
298 OpenExcerpts,
299 RestartLanguageServer,
300 Hover,
301 Format,
302 ToggleSoftWrap,
303 ToggleInlayHints,
304 RevealInFinder,
305 CopyPath,
306 CopyRelativePath,
307 CopyHighlightJson,
308 ]
309);
310
311impl_actions!(
312 editor,
313 [
314 SelectNext,
315 SelectPrevious,
316 SelectToBeginningOfLine,
317 SelectToEndOfLine,
318 ToggleCodeActions,
319 MovePageUp,
320 MovePageDown,
321 ConfirmCompletion,
322 ConfirmCodeAction,
323 ToggleComments,
324 FoldAt,
325 UnfoldAt,
326 GutterHover
327 ]
328);
329
330enum DocumentHighlightRead {}
331enum DocumentHighlightWrite {}
332enum InputComposition {}
333
334#[derive(Copy, Clone, PartialEq, Eq)]
335pub enum Direction {
336 Prev,
337 Next,
338}
339
340pub fn init_settings(cx: &mut AppContext) {
341 settings::register::<EditorSettings>(cx);
342}
343
344pub fn init(cx: &mut AppContext) {
345 init_settings(cx);
346 cx.add_action(Editor::new_file);
347 cx.add_action(Editor::cancel);
348 cx.add_action(Editor::newline);
349 cx.add_action(Editor::newline_above);
350 cx.add_action(Editor::newline_below);
351 cx.add_action(Editor::backspace);
352 cx.add_action(Editor::delete);
353 cx.add_action(Editor::tab);
354 cx.add_action(Editor::tab_prev);
355 cx.add_action(Editor::indent);
356 cx.add_action(Editor::outdent);
357 cx.add_action(Editor::delete_line);
358 cx.add_action(Editor::join_lines);
359 cx.add_action(Editor::sort_lines_case_sensitive);
360 cx.add_action(Editor::sort_lines_case_insensitive);
361 cx.add_action(Editor::reverse_lines);
362 cx.add_action(Editor::shuffle_lines);
363 cx.add_action(Editor::convert_to_upper_case);
364 cx.add_action(Editor::convert_to_lower_case);
365 cx.add_action(Editor::convert_to_title_case);
366 cx.add_action(Editor::convert_to_snake_case);
367 cx.add_action(Editor::convert_to_kebab_case);
368 cx.add_action(Editor::convert_to_upper_camel_case);
369 cx.add_action(Editor::convert_to_lower_camel_case);
370 cx.add_action(Editor::delete_to_previous_word_start);
371 cx.add_action(Editor::delete_to_previous_subword_start);
372 cx.add_action(Editor::delete_to_next_word_end);
373 cx.add_action(Editor::delete_to_next_subword_end);
374 cx.add_action(Editor::delete_to_beginning_of_line);
375 cx.add_action(Editor::delete_to_end_of_line);
376 cx.add_action(Editor::cut_to_end_of_line);
377 cx.add_action(Editor::duplicate_line);
378 cx.add_action(Editor::move_line_up);
379 cx.add_action(Editor::move_line_down);
380 cx.add_action(Editor::transpose);
381 cx.add_action(Editor::cut);
382 cx.add_action(Editor::copy);
383 cx.add_action(Editor::paste);
384 cx.add_action(Editor::undo);
385 cx.add_action(Editor::redo);
386 cx.add_action(Editor::move_up);
387 cx.add_action(Editor::move_page_up);
388 cx.add_action(Editor::move_down);
389 cx.add_action(Editor::move_page_down);
390 cx.add_action(Editor::next_screen);
391 cx.add_action(Editor::move_left);
392 cx.add_action(Editor::move_right);
393 cx.add_action(Editor::move_to_previous_word_start);
394 cx.add_action(Editor::move_to_previous_subword_start);
395 cx.add_action(Editor::move_to_next_word_end);
396 cx.add_action(Editor::move_to_next_subword_end);
397 cx.add_action(Editor::move_to_beginning_of_line);
398 cx.add_action(Editor::move_to_end_of_line);
399 cx.add_action(Editor::move_to_start_of_paragraph);
400 cx.add_action(Editor::move_to_end_of_paragraph);
401 cx.add_action(Editor::move_to_beginning);
402 cx.add_action(Editor::move_to_end);
403 cx.add_action(Editor::select_up);
404 cx.add_action(Editor::select_down);
405 cx.add_action(Editor::select_left);
406 cx.add_action(Editor::select_right);
407 cx.add_action(Editor::select_to_previous_word_start);
408 cx.add_action(Editor::select_to_previous_subword_start);
409 cx.add_action(Editor::select_to_next_word_end);
410 cx.add_action(Editor::select_to_next_subword_end);
411 cx.add_action(Editor::select_to_beginning_of_line);
412 cx.add_action(Editor::select_to_end_of_line);
413 cx.add_action(Editor::select_to_start_of_paragraph);
414 cx.add_action(Editor::select_to_end_of_paragraph);
415 cx.add_action(Editor::select_to_beginning);
416 cx.add_action(Editor::select_to_end);
417 cx.add_action(Editor::select_all);
418 cx.add_action(Editor::select_line);
419 cx.add_action(Editor::split_selection_into_lines);
420 cx.add_action(Editor::add_selection_above);
421 cx.add_action(Editor::add_selection_below);
422 cx.add_action(Editor::select_next);
423 cx.add_action(Editor::select_previous);
424 cx.add_action(Editor::toggle_comments);
425 cx.add_action(Editor::select_larger_syntax_node);
426 cx.add_action(Editor::select_smaller_syntax_node);
427 cx.add_action(Editor::move_to_enclosing_bracket);
428 cx.add_action(Editor::undo_selection);
429 cx.add_action(Editor::redo_selection);
430 cx.add_action(Editor::go_to_diagnostic);
431 cx.add_action(Editor::go_to_prev_diagnostic);
432 cx.add_action(Editor::go_to_hunk);
433 cx.add_action(Editor::go_to_prev_hunk);
434 cx.add_action(Editor::go_to_definition);
435 cx.add_action(Editor::go_to_definition_split);
436 cx.add_action(Editor::go_to_type_definition);
437 cx.add_action(Editor::go_to_type_definition_split);
438 cx.add_action(Editor::fold);
439 cx.add_action(Editor::fold_at);
440 cx.add_action(Editor::unfold_lines);
441 cx.add_action(Editor::unfold_at);
442 cx.add_action(Editor::gutter_hover);
443 cx.add_action(Editor::fold_selected_ranges);
444 cx.add_action(Editor::show_completions);
445 cx.add_action(Editor::toggle_code_actions);
446 cx.add_action(Editor::open_excerpts);
447 cx.add_action(Editor::toggle_soft_wrap);
448 cx.add_action(Editor::toggle_inlay_hints);
449 cx.add_action(Editor::reveal_in_finder);
450 cx.add_action(Editor::copy_path);
451 cx.add_action(Editor::copy_relative_path);
452 cx.add_action(Editor::copy_highlight_json);
453 cx.add_async_action(Editor::format);
454 cx.add_action(Editor::restart_language_server);
455 cx.add_action(Editor::show_character_palette);
456 cx.add_async_action(Editor::confirm_completion);
457 cx.add_async_action(Editor::confirm_code_action);
458 cx.add_async_action(Editor::rename);
459 cx.add_async_action(Editor::confirm_rename);
460 cx.add_async_action(Editor::find_all_references);
461 cx.add_action(Editor::next_copilot_suggestion);
462 cx.add_action(Editor::previous_copilot_suggestion);
463 cx.add_action(Editor::copilot_suggest);
464
465 hover_popover::init(cx);
466 scroll::actions::init(cx);
467
468 workspace::register_project_item::<Editor>(cx);
469 workspace::register_followable_item::<Editor>(cx);
470 workspace::register_deserializable_item::<Editor>(cx);
471}
472
473trait InvalidationRegion {
474 fn ranges(&self) -> &[Range<Anchor>];
475}
476
477#[derive(Clone, Debug, PartialEq)]
478pub enum SelectPhase {
479 Begin {
480 position: DisplayPoint,
481 add: bool,
482 click_count: usize,
483 },
484 BeginColumnar {
485 position: DisplayPoint,
486 goal_column: u32,
487 },
488 Extend {
489 position: DisplayPoint,
490 click_count: usize,
491 },
492 Update {
493 position: DisplayPoint,
494 goal_column: u32,
495 scroll_position: Vector2F,
496 },
497 End,
498}
499
500#[derive(Clone, Debug)]
501pub enum SelectMode {
502 Character,
503 Word(Range<Anchor>),
504 Line(Range<Anchor>),
505 All,
506}
507
508#[derive(Copy, Clone, PartialEq, Eq, Debug)]
509pub enum EditorMode {
510 SingleLine,
511 AutoHeight { max_lines: usize },
512 Full,
513}
514
515#[derive(Clone, Debug)]
516pub enum SoftWrap {
517 None,
518 EditorWidth,
519 Column(u32),
520}
521
522#[derive(Clone)]
523pub struct EditorStyle {
524 pub text: TextStyle,
525 pub line_height_scalar: f32,
526 pub placeholder_text: Option<TextStyle>,
527 pub theme: theme::Editor,
528 pub theme_id: usize,
529}
530
531type CompletionId = usize;
532
533type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
534type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
535
536pub struct Editor {
537 handle: WeakViewHandle<Self>,
538 buffer: ModelHandle<MultiBuffer>,
539 display_map: ModelHandle<DisplayMap>,
540 pub selections: SelectionsCollection,
541 pub scroll_manager: ScrollManager,
542 columnar_selection_tail: Option<Anchor>,
543 add_selections_state: Option<AddSelectionsState>,
544 select_next_state: Option<SelectNextState>,
545 select_prev_state: Option<SelectNextState>,
546 selection_history: SelectionHistory,
547 autoclose_regions: Vec<AutocloseRegion>,
548 snippet_stack: InvalidationStack<SnippetState>,
549 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
550 ime_transaction: Option<TransactionId>,
551 active_diagnostics: Option<ActiveDiagnosticGroup>,
552 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
553 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
554 override_text_style: Option<Box<OverrideTextStyle>>,
555 project: Option<ModelHandle<Project>>,
556 focused: bool,
557 blink_manager: ModelHandle<BlinkManager>,
558 show_local_selections: bool,
559 mode: EditorMode,
560 replica_id_mapping: Option<HashMap<ReplicaId, ReplicaId>>,
561 show_gutter: bool,
562 show_wrap_guides: Option<bool>,
563 placeholder_text: Option<Arc<str>>,
564 highlighted_rows: Option<Range<u32>>,
565 #[allow(clippy::type_complexity)]
566 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
567 nav_history: Option<ItemNavHistory>,
568 context_menu: Option<ContextMenu>,
569 mouse_context_menu: ViewHandle<context_menu::ContextMenu>,
570 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
571 next_completion_id: CompletionId,
572 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
573 code_actions_task: Option<Task<()>>,
574 document_highlights_task: Option<Task<()>>,
575 pending_rename: Option<RenameState>,
576 searchable: bool,
577 cursor_shape: CursorShape,
578 collapse_matches: bool,
579 autoindent_mode: Option<AutoindentMode>,
580 workspace: Option<(WeakViewHandle<Workspace>, i64)>,
581 keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
582 input_enabled: bool,
583 read_only: bool,
584 leader_replica_id: Option<u16>,
585 remote_id: Option<ViewId>,
586 hover_state: HoverState,
587 gutter_hovered: bool,
588 link_go_to_definition_state: LinkGoToDefinitionState,
589 copilot_state: CopilotState,
590 inlay_hint_cache: InlayHintCache,
591 next_inlay_id: usize,
592 _subscriptions: Vec<Subscription>,
593 pixel_position_of_newest_cursor: Option<Vector2F>,
594}
595
596pub struct EditorSnapshot {
597 pub mode: EditorMode,
598 pub show_gutter: bool,
599 pub display_snapshot: DisplaySnapshot,
600 pub placeholder_text: Option<Arc<str>>,
601 is_focused: bool,
602 scroll_anchor: ScrollAnchor,
603 ongoing_scroll: OngoingScroll,
604}
605
606#[derive(Clone, Debug)]
607struct SelectionHistoryEntry {
608 selections: Arc<[Selection<Anchor>]>,
609 select_next_state: Option<SelectNextState>,
610 select_prev_state: Option<SelectNextState>,
611 add_selections_state: Option<AddSelectionsState>,
612}
613
614enum SelectionHistoryMode {
615 Normal,
616 Undoing,
617 Redoing,
618}
619
620impl Default for SelectionHistoryMode {
621 fn default() -> Self {
622 Self::Normal
623 }
624}
625
626#[derive(Default)]
627struct SelectionHistory {
628 #[allow(clippy::type_complexity)]
629 selections_by_transaction:
630 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
631 mode: SelectionHistoryMode,
632 undo_stack: VecDeque<SelectionHistoryEntry>,
633 redo_stack: VecDeque<SelectionHistoryEntry>,
634}
635
636impl SelectionHistory {
637 fn insert_transaction(
638 &mut self,
639 transaction_id: TransactionId,
640 selections: Arc<[Selection<Anchor>]>,
641 ) {
642 self.selections_by_transaction
643 .insert(transaction_id, (selections, None));
644 }
645
646 #[allow(clippy::type_complexity)]
647 fn transaction(
648 &self,
649 transaction_id: TransactionId,
650 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
651 self.selections_by_transaction.get(&transaction_id)
652 }
653
654 #[allow(clippy::type_complexity)]
655 fn transaction_mut(
656 &mut self,
657 transaction_id: TransactionId,
658 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
659 self.selections_by_transaction.get_mut(&transaction_id)
660 }
661
662 fn push(&mut self, entry: SelectionHistoryEntry) {
663 if !entry.selections.is_empty() {
664 match self.mode {
665 SelectionHistoryMode::Normal => {
666 self.push_undo(entry);
667 self.redo_stack.clear();
668 }
669 SelectionHistoryMode::Undoing => self.push_redo(entry),
670 SelectionHistoryMode::Redoing => self.push_undo(entry),
671 }
672 }
673 }
674
675 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
676 if self
677 .undo_stack
678 .back()
679 .map_or(true, |e| e.selections != entry.selections)
680 {
681 self.undo_stack.push_back(entry);
682 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
683 self.undo_stack.pop_front();
684 }
685 }
686 }
687
688 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
689 if self
690 .redo_stack
691 .back()
692 .map_or(true, |e| e.selections != entry.selections)
693 {
694 self.redo_stack.push_back(entry);
695 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
696 self.redo_stack.pop_front();
697 }
698 }
699 }
700}
701
702#[derive(Clone, Debug)]
703struct AddSelectionsState {
704 above: bool,
705 stack: Vec<usize>,
706}
707
708#[derive(Clone, Debug)]
709struct SelectNextState {
710 query: AhoCorasick,
711 wordwise: bool,
712 done: bool,
713}
714
715#[derive(Debug)]
716struct AutocloseRegion {
717 selection_id: usize,
718 range: Range<Anchor>,
719 pair: BracketPair,
720}
721
722#[derive(Debug)]
723struct SnippetState {
724 ranges: Vec<Vec<Range<Anchor>>>,
725 active_index: usize,
726}
727
728pub struct RenameState {
729 pub range: Range<Anchor>,
730 pub old_name: Arc<str>,
731 pub editor: ViewHandle<Editor>,
732 block_id: BlockId,
733}
734
735struct InvalidationStack<T>(Vec<T>);
736
737enum ContextMenu {
738 Completions(CompletionsMenu),
739 CodeActions(CodeActionsMenu),
740}
741
742impl ContextMenu {
743 fn select_first(&mut self, cx: &mut ViewContext<Editor>) -> bool {
744 if self.visible() {
745 match self {
746 ContextMenu::Completions(menu) => menu.select_first(cx),
747 ContextMenu::CodeActions(menu) => menu.select_first(cx),
748 }
749 true
750 } else {
751 false
752 }
753 }
754
755 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
756 if self.visible() {
757 match self {
758 ContextMenu::Completions(menu) => menu.select_prev(cx),
759 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
760 }
761 true
762 } else {
763 false
764 }
765 }
766
767 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
768 if self.visible() {
769 match self {
770 ContextMenu::Completions(menu) => menu.select_next(cx),
771 ContextMenu::CodeActions(menu) => menu.select_next(cx),
772 }
773 true
774 } else {
775 false
776 }
777 }
778
779 fn select_last(&mut self, cx: &mut ViewContext<Editor>) -> bool {
780 if self.visible() {
781 match self {
782 ContextMenu::Completions(menu) => menu.select_last(cx),
783 ContextMenu::CodeActions(menu) => menu.select_last(cx),
784 }
785 true
786 } else {
787 false
788 }
789 }
790
791 fn visible(&self) -> bool {
792 match self {
793 ContextMenu::Completions(menu) => menu.visible(),
794 ContextMenu::CodeActions(menu) => menu.visible(),
795 }
796 }
797
798 fn render(
799 &self,
800 cursor_position: DisplayPoint,
801 style: EditorStyle,
802 cx: &mut ViewContext<Editor>,
803 ) -> (DisplayPoint, AnyElement<Editor>) {
804 match self {
805 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
806 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
807 }
808 }
809}
810
811struct CompletionsMenu {
812 id: CompletionId,
813 initial_position: Anchor,
814 buffer: ModelHandle<Buffer>,
815 completions: Arc<[Completion]>,
816 match_candidates: Vec<StringMatchCandidate>,
817 matches: Arc<[StringMatch]>,
818 selected_item: usize,
819 list: UniformListState,
820}
821
822impl CompletionsMenu {
823 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
824 self.selected_item = 0;
825 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
826 cx.notify();
827 }
828
829 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
830 if self.selected_item > 0 {
831 self.selected_item -= 1;
832 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
833 }
834 cx.notify();
835 }
836
837 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
838 if self.selected_item + 1 < self.matches.len() {
839 self.selected_item += 1;
840 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
841 }
842 cx.notify();
843 }
844
845 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
846 self.selected_item = self.matches.len() - 1;
847 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
848 cx.notify();
849 }
850
851 fn visible(&self) -> bool {
852 !self.matches.is_empty()
853 }
854
855 fn render(&self, style: EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
856 enum CompletionTag {}
857
858 let completions = self.completions.clone();
859 let matches = self.matches.clone();
860 let selected_item = self.selected_item;
861 let container_style = style.autocomplete.container;
862 UniformList::new(
863 self.list.clone(),
864 matches.len(),
865 cx,
866 move |_, range, items, cx| {
867 let start_ix = range.start;
868 for (ix, mat) in matches[range].iter().enumerate() {
869 let completion = &completions[mat.candidate_id];
870 let item_ix = start_ix + ix;
871 items.push(
872 MouseEventHandler::new::<CompletionTag, _>(
873 mat.candidate_id,
874 cx,
875 |state, _| {
876 let item_style = if item_ix == selected_item {
877 style.autocomplete.selected_item
878 } else if state.hovered() {
879 style.autocomplete.hovered_item
880 } else {
881 style.autocomplete.item
882 };
883
884 Text::new(completion.label.text.clone(), style.text.clone())
885 .with_soft_wrap(false)
886 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
887 &completion.label.text,
888 style.text.color.into(),
889 styled_runs_for_code_label(
890 &completion.label,
891 &style.syntax,
892 ),
893 &mat.positions,
894 ))
895 .contained()
896 .with_style(item_style)
897 },
898 )
899 .with_cursor_style(CursorStyle::PointingHand)
900 .on_down(MouseButton::Left, move |_, this, cx| {
901 this.confirm_completion(
902 &ConfirmCompletion {
903 item_ix: Some(item_ix),
904 },
905 cx,
906 );
907 })
908 .into_any(),
909 );
910 }
911 },
912 )
913 .with_width_from_item(
914 self.matches
915 .iter()
916 .enumerate()
917 .max_by_key(|(_, mat)| {
918 self.completions[mat.candidate_id]
919 .label
920 .text
921 .chars()
922 .count()
923 })
924 .map(|(ix, _)| ix),
925 )
926 .contained()
927 .with_style(container_style)
928 .into_any()
929 }
930
931 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
932 let mut matches = if let Some(query) = query {
933 fuzzy::match_strings(
934 &self.match_candidates,
935 query,
936 query.chars().any(|c| c.is_uppercase()),
937 100,
938 &Default::default(),
939 executor,
940 )
941 .await
942 } else {
943 self.match_candidates
944 .iter()
945 .enumerate()
946 .map(|(candidate_id, candidate)| StringMatch {
947 candidate_id,
948 score: Default::default(),
949 positions: Default::default(),
950 string: candidate.string.clone(),
951 })
952 .collect()
953 };
954
955 //Remove all candidates where the query's start does not match the start of any word in the candidate
956 if let Some(query) = query {
957 if let Some(query_start) = query.chars().next() {
958 matches.retain(|string_match| {
959 split_words(&string_match.string).any(|word| {
960 //Check that the first codepoint of the word as lowercase matches the first
961 //codepoint of the query as lowercase
962 word.chars()
963 .flat_map(|codepoint| codepoint.to_lowercase())
964 .zip(query_start.to_lowercase())
965 .all(|(word_cp, query_cp)| word_cp == query_cp)
966 })
967 });
968 }
969 }
970
971 matches.sort_unstable_by_key(|mat| {
972 let completion = &self.completions[mat.candidate_id];
973 (
974 completion.lsp_completion.sort_text.as_ref(),
975 Reverse(OrderedFloat(mat.score)),
976 completion.sort_key(),
977 )
978 });
979
980 for mat in &mut matches {
981 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
982 for position in &mut mat.positions {
983 *position += filter_start;
984 }
985 }
986
987 self.matches = matches.into();
988 }
989}
990
991#[derive(Clone)]
992struct CodeActionsMenu {
993 actions: Arc<[CodeAction]>,
994 buffer: ModelHandle<Buffer>,
995 selected_item: usize,
996 list: UniformListState,
997 deployed_from_indicator: bool,
998}
999
1000impl CodeActionsMenu {
1001 fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
1002 self.selected_item = 0;
1003 cx.notify()
1004 }
1005
1006 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
1007 if self.selected_item > 0 {
1008 self.selected_item -= 1;
1009 cx.notify()
1010 }
1011 }
1012
1013 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
1014 if self.selected_item + 1 < self.actions.len() {
1015 self.selected_item += 1;
1016 cx.notify()
1017 }
1018 }
1019
1020 fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
1021 self.selected_item = self.actions.len() - 1;
1022 cx.notify()
1023 }
1024
1025 fn visible(&self) -> bool {
1026 !self.actions.is_empty()
1027 }
1028
1029 fn render(
1030 &self,
1031 mut cursor_position: DisplayPoint,
1032 style: EditorStyle,
1033 cx: &mut ViewContext<Editor>,
1034 ) -> (DisplayPoint, AnyElement<Editor>) {
1035 enum ActionTag {}
1036
1037 let container_style = style.autocomplete.container;
1038 let actions = self.actions.clone();
1039 let selected_item = self.selected_item;
1040 let element = UniformList::new(
1041 self.list.clone(),
1042 actions.len(),
1043 cx,
1044 move |_, range, items, cx| {
1045 let start_ix = range.start;
1046 for (ix, action) in actions[range].iter().enumerate() {
1047 let item_ix = start_ix + ix;
1048 items.push(
1049 MouseEventHandler::new::<ActionTag, _>(item_ix, cx, |state, _| {
1050 let item_style = if item_ix == selected_item {
1051 style.autocomplete.selected_item
1052 } else if state.hovered() {
1053 style.autocomplete.hovered_item
1054 } else {
1055 style.autocomplete.item
1056 };
1057
1058 Text::new(action.lsp_action.title.clone(), style.text.clone())
1059 .with_soft_wrap(false)
1060 .contained()
1061 .with_style(item_style)
1062 })
1063 .with_cursor_style(CursorStyle::PointingHand)
1064 .on_down(MouseButton::Left, move |_, this, cx| {
1065 let workspace = this
1066 .workspace
1067 .as_ref()
1068 .and_then(|(workspace, _)| workspace.upgrade(cx));
1069 cx.window_context().defer(move |cx| {
1070 if let Some(workspace) = workspace {
1071 workspace.update(cx, |workspace, cx| {
1072 if let Some(task) = Editor::confirm_code_action(
1073 workspace,
1074 &Default::default(),
1075 cx,
1076 ) {
1077 task.detach_and_log_err(cx);
1078 }
1079 });
1080 }
1081 });
1082 })
1083 .into_any(),
1084 );
1085 }
1086 },
1087 )
1088 .with_width_from_item(
1089 self.actions
1090 .iter()
1091 .enumerate()
1092 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
1093 .map(|(ix, _)| ix),
1094 )
1095 .contained()
1096 .with_style(container_style)
1097 .into_any();
1098
1099 if self.deployed_from_indicator {
1100 *cursor_position.column_mut() = 0;
1101 }
1102
1103 (cursor_position, element)
1104 }
1105}
1106
1107pub struct CopilotState {
1108 excerpt_id: Option<ExcerptId>,
1109 pending_refresh: Task<Option<()>>,
1110 pending_cycling_refresh: Task<Option<()>>,
1111 cycled: bool,
1112 completions: Vec<copilot::Completion>,
1113 active_completion_index: usize,
1114 suggestion: Option<Inlay>,
1115}
1116
1117impl Default for CopilotState {
1118 fn default() -> Self {
1119 Self {
1120 excerpt_id: None,
1121 pending_cycling_refresh: Task::ready(Some(())),
1122 pending_refresh: Task::ready(Some(())),
1123 completions: Default::default(),
1124 active_completion_index: 0,
1125 cycled: false,
1126 suggestion: None,
1127 }
1128 }
1129}
1130
1131impl CopilotState {
1132 fn active_completion(&self) -> Option<&copilot::Completion> {
1133 self.completions.get(self.active_completion_index)
1134 }
1135
1136 fn text_for_active_completion(
1137 &self,
1138 cursor: Anchor,
1139 buffer: &MultiBufferSnapshot,
1140 ) -> Option<&str> {
1141 use language::ToOffset as _;
1142
1143 let completion = self.active_completion()?;
1144 let excerpt_id = self.excerpt_id?;
1145 let completion_buffer = buffer.buffer_for_excerpt(excerpt_id)?;
1146 if excerpt_id != cursor.excerpt_id
1147 || !completion.range.start.is_valid(completion_buffer)
1148 || !completion.range.end.is_valid(completion_buffer)
1149 {
1150 return None;
1151 }
1152
1153 let mut completion_range = completion.range.to_offset(&completion_buffer);
1154 let prefix_len = Self::common_prefix(
1155 completion_buffer.chars_for_range(completion_range.clone()),
1156 completion.text.chars(),
1157 );
1158 completion_range.start += prefix_len;
1159 let suffix_len = Self::common_prefix(
1160 completion_buffer.reversed_chars_for_range(completion_range.clone()),
1161 completion.text[prefix_len..].chars().rev(),
1162 );
1163 completion_range.end = completion_range.end.saturating_sub(suffix_len);
1164
1165 if completion_range.is_empty()
1166 && completion_range.start == cursor.text_anchor.to_offset(&completion_buffer)
1167 {
1168 Some(&completion.text[prefix_len..completion.text.len() - suffix_len])
1169 } else {
1170 None
1171 }
1172 }
1173
1174 fn cycle_completions(&mut self, direction: Direction) {
1175 match direction {
1176 Direction::Prev => {
1177 self.active_completion_index = if self.active_completion_index == 0 {
1178 self.completions.len().saturating_sub(1)
1179 } else {
1180 self.active_completion_index - 1
1181 };
1182 }
1183 Direction::Next => {
1184 if self.completions.len() == 0 {
1185 self.active_completion_index = 0
1186 } else {
1187 self.active_completion_index =
1188 (self.active_completion_index + 1) % self.completions.len();
1189 }
1190 }
1191 }
1192 }
1193
1194 fn push_completion(&mut self, new_completion: copilot::Completion) {
1195 for completion in &self.completions {
1196 if completion.text == new_completion.text && completion.range == new_completion.range {
1197 return;
1198 }
1199 }
1200 self.completions.push(new_completion);
1201 }
1202
1203 fn common_prefix<T1: Iterator<Item = char>, T2: Iterator<Item = char>>(a: T1, b: T2) -> usize {
1204 a.zip(b)
1205 .take_while(|(a, b)| a == b)
1206 .map(|(a, _)| a.len_utf8())
1207 .sum()
1208 }
1209}
1210
1211#[derive(Debug)]
1212struct ActiveDiagnosticGroup {
1213 primary_range: Range<Anchor>,
1214 primary_message: String,
1215 blocks: HashMap<BlockId, Diagnostic>,
1216 is_valid: bool,
1217}
1218
1219#[derive(Serialize, Deserialize)]
1220pub struct ClipboardSelection {
1221 pub len: usize,
1222 pub is_entire_line: bool,
1223 pub first_line_indent: u32,
1224}
1225
1226#[derive(Debug)]
1227pub struct NavigationData {
1228 cursor_anchor: Anchor,
1229 cursor_position: Point,
1230 scroll_anchor: ScrollAnchor,
1231 scroll_top_row: u32,
1232}
1233
1234pub struct EditorCreated(pub ViewHandle<Editor>);
1235
1236enum GotoDefinitionKind {
1237 Symbol,
1238 Type,
1239}
1240
1241#[derive(Debug, Clone)]
1242enum InlayHintRefreshReason {
1243 Toggle(bool),
1244 SettingsChange(InlayHintSettings),
1245 NewLinesShown,
1246 BufferEdited(HashSet<Arc<Language>>),
1247 RefreshRequested,
1248}
1249
1250impl Editor {
1251 pub fn single_line(
1252 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1253 cx: &mut ViewContext<Self>,
1254 ) -> Self {
1255 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1256 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1257 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
1258 }
1259
1260 pub fn multi_line(
1261 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1262 cx: &mut ViewContext<Self>,
1263 ) -> Self {
1264 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1265 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1266 Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
1267 }
1268
1269 pub fn auto_height(
1270 max_lines: usize,
1271 field_editor_style: Option<Arc<GetFieldEditorTheme>>,
1272 cx: &mut ViewContext<Self>,
1273 ) -> Self {
1274 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
1275 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1276 Self::new(
1277 EditorMode::AutoHeight { max_lines },
1278 buffer,
1279 None,
1280 field_editor_style,
1281 cx,
1282 )
1283 }
1284
1285 pub fn for_buffer(
1286 buffer: ModelHandle<Buffer>,
1287 project: Option<ModelHandle<Project>>,
1288 cx: &mut ViewContext<Self>,
1289 ) -> Self {
1290 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1291 Self::new(EditorMode::Full, buffer, project, None, cx)
1292 }
1293
1294 pub fn for_multibuffer(
1295 buffer: ModelHandle<MultiBuffer>,
1296 project: Option<ModelHandle<Project>>,
1297 cx: &mut ViewContext<Self>,
1298 ) -> Self {
1299 Self::new(EditorMode::Full, buffer, project, None, cx)
1300 }
1301
1302 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
1303 let mut clone = Self::new(
1304 self.mode,
1305 self.buffer.clone(),
1306 self.project.clone(),
1307 self.get_field_editor_theme.clone(),
1308 cx,
1309 );
1310 self.display_map.update(cx, |display_map, cx| {
1311 let snapshot = display_map.snapshot(cx);
1312 clone.display_map.update(cx, |display_map, cx| {
1313 display_map.set_state(&snapshot, cx);
1314 });
1315 });
1316 clone.selections.clone_state(&self.selections);
1317 clone.scroll_manager.clone_state(&self.scroll_manager);
1318 clone.searchable = self.searchable;
1319 clone
1320 }
1321
1322 fn new(
1323 mode: EditorMode,
1324 buffer: ModelHandle<MultiBuffer>,
1325 project: Option<ModelHandle<Project>>,
1326 get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
1327 cx: &mut ViewContext<Self>,
1328 ) -> Self {
1329 let editor_view_id = cx.view_id();
1330 let display_map = cx.add_model(|cx| {
1331 let settings = settings::get::<ThemeSettings>(cx);
1332 let style = build_style(settings, get_field_editor_theme.as_deref(), None, cx);
1333 DisplayMap::new(
1334 buffer.clone(),
1335 style.text.font_id,
1336 style.text.font_size,
1337 None,
1338 2,
1339 1,
1340 cx,
1341 )
1342 });
1343
1344 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1345
1346 let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1347
1348 let soft_wrap_mode_override =
1349 (mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
1350
1351 let mut project_subscriptions = Vec::new();
1352 if mode == EditorMode::Full {
1353 if let Some(project) = project.as_ref() {
1354 if buffer.read(cx).is_singleton() {
1355 project_subscriptions.push(cx.observe(project, |_, _, cx| {
1356 cx.emit(Event::TitleChanged);
1357 }));
1358 }
1359 project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
1360 if let project::Event::RefreshInlayHints = event {
1361 editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1362 };
1363 }));
1364 }
1365 }
1366
1367 let inlay_hint_settings = inlay_hint_settings(
1368 selections.newest_anchor().head(),
1369 &buffer.read(cx).snapshot(cx),
1370 cx,
1371 );
1372
1373 let mut this = Self {
1374 handle: cx.weak_handle(),
1375 buffer: buffer.clone(),
1376 display_map: display_map.clone(),
1377 selections,
1378 scroll_manager: ScrollManager::new(),
1379 columnar_selection_tail: None,
1380 add_selections_state: None,
1381 select_next_state: None,
1382 select_prev_state: None,
1383 selection_history: Default::default(),
1384 autoclose_regions: Default::default(),
1385 snippet_stack: Default::default(),
1386 select_larger_syntax_node_stack: Vec::new(),
1387 ime_transaction: Default::default(),
1388 active_diagnostics: None,
1389 soft_wrap_mode_override,
1390 get_field_editor_theme,
1391 project,
1392 focused: false,
1393 blink_manager: blink_manager.clone(),
1394 show_local_selections: true,
1395 mode,
1396 replica_id_mapping: None,
1397 show_gutter: mode == EditorMode::Full,
1398 show_wrap_guides: None,
1399 placeholder_text: None,
1400 highlighted_rows: None,
1401 background_highlights: Default::default(),
1402 nav_history: None,
1403 context_menu: None,
1404 mouse_context_menu: cx
1405 .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
1406 completion_tasks: Default::default(),
1407 next_completion_id: 0,
1408 next_inlay_id: 0,
1409 available_code_actions: Default::default(),
1410 code_actions_task: Default::default(),
1411 document_highlights_task: Default::default(),
1412 pending_rename: Default::default(),
1413 searchable: true,
1414 override_text_style: None,
1415 cursor_shape: Default::default(),
1416 autoindent_mode: Some(AutoindentMode::EachLine),
1417 collapse_matches: false,
1418 workspace: None,
1419 keymap_context_layers: Default::default(),
1420 input_enabled: true,
1421 read_only: false,
1422 leader_replica_id: None,
1423 remote_id: None,
1424 hover_state: Default::default(),
1425 link_go_to_definition_state: Default::default(),
1426 copilot_state: Default::default(),
1427 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1428 gutter_hovered: false,
1429 pixel_position_of_newest_cursor: None,
1430 _subscriptions: vec![
1431 cx.observe(&buffer, Self::on_buffer_changed),
1432 cx.subscribe(&buffer, Self::on_buffer_event),
1433 cx.observe(&display_map, Self::on_display_map_changed),
1434 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1435 cx.observe_global::<SettingsStore, _>(Self::settings_changed),
1436 ],
1437 };
1438
1439 this._subscriptions.extend(project_subscriptions);
1440
1441 this.end_selection(cx);
1442 this.scroll_manager.show_scrollbar(cx);
1443
1444 let editor_created_event = EditorCreated(cx.handle());
1445 cx.emit_global(editor_created_event);
1446
1447 if mode == EditorMode::Full {
1448 let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars();
1449 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1450 }
1451
1452 this.report_editor_event("open", None, cx);
1453 this
1454 }
1455
1456 pub fn new_file(
1457 workspace: &mut Workspace,
1458 _: &workspace::NewFile,
1459 cx: &mut ViewContext<Workspace>,
1460 ) {
1461 let project = workspace.project().clone();
1462 if project.read(cx).is_remote() {
1463 cx.propagate_action();
1464 } else if let Some(buffer) = project
1465 .update(cx, |project, cx| project.create_buffer("", None, cx))
1466 .log_err()
1467 {
1468 workspace.add_item(
1469 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1470 cx,
1471 );
1472 }
1473 }
1474
1475 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1476 self.buffer.read(cx).replica_id()
1477 }
1478
1479 pub fn leader_replica_id(&self) -> Option<ReplicaId> {
1480 self.leader_replica_id
1481 }
1482
1483 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1484 &self.buffer
1485 }
1486
1487 fn workspace(&self, cx: &AppContext) -> Option<ViewHandle<Workspace>> {
1488 self.workspace.as_ref()?.0.upgrade(cx)
1489 }
1490
1491 pub fn title<'a>(&self, cx: &'a AppContext) -> Cow<'a, str> {
1492 self.buffer().read(cx).title(cx)
1493 }
1494
1495 pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
1496 EditorSnapshot {
1497 mode: self.mode,
1498 show_gutter: self.show_gutter,
1499 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1500 scroll_anchor: self.scroll_manager.anchor(),
1501 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1502 placeholder_text: self.placeholder_text.clone(),
1503 is_focused: self
1504 .handle
1505 .upgrade(cx)
1506 .map_or(false, |handle| handle.is_focused(cx)),
1507 }
1508 }
1509
1510 pub fn language_at<'a, T: ToOffset>(
1511 &self,
1512 point: T,
1513 cx: &'a AppContext,
1514 ) -> Option<Arc<Language>> {
1515 self.buffer.read(cx).language_at(point, cx)
1516 }
1517
1518 pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> {
1519 self.buffer.read(cx).read(cx).file_at(point).cloned()
1520 }
1521
1522 pub fn active_excerpt(
1523 &self,
1524 cx: &AppContext,
1525 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1526 self.buffer
1527 .read(cx)
1528 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1529 }
1530
1531 fn style(&self, cx: &AppContext) -> EditorStyle {
1532 build_style(
1533 settings::get::<ThemeSettings>(cx),
1534 self.get_field_editor_theme.as_deref(),
1535 self.override_text_style.as_deref(),
1536 cx,
1537 )
1538 }
1539
1540 pub fn mode(&self) -> EditorMode {
1541 self.mode
1542 }
1543
1544 pub fn set_placeholder_text(
1545 &mut self,
1546 placeholder_text: impl Into<Arc<str>>,
1547 cx: &mut ViewContext<Self>,
1548 ) {
1549 self.placeholder_text = Some(placeholder_text.into());
1550 cx.notify();
1551 }
1552
1553 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1554 self.cursor_shape = cursor_shape;
1555 cx.notify();
1556 }
1557
1558 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1559 self.collapse_matches = collapse_matches;
1560 }
1561
1562 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1563 if self.collapse_matches {
1564 return range.start..range.start;
1565 }
1566 range.clone()
1567 }
1568
1569 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1570 if self.display_map.read(cx).clip_at_line_ends != clip {
1571 self.display_map
1572 .update(cx, |map, _| map.clip_at_line_ends = clip);
1573 }
1574 }
1575
1576 pub fn set_keymap_context_layer<Tag: 'static>(
1577 &mut self,
1578 context: KeymapContext,
1579 cx: &mut ViewContext<Self>,
1580 ) {
1581 self.keymap_context_layers
1582 .insert(TypeId::of::<Tag>(), context);
1583 cx.notify();
1584 }
1585
1586 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
1587 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1588 cx.notify();
1589 }
1590
1591 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1592 self.input_enabled = input_enabled;
1593 }
1594
1595 pub fn set_autoindent(&mut self, autoindent: bool) {
1596 if autoindent {
1597 self.autoindent_mode = Some(AutoindentMode::EachLine);
1598 } else {
1599 self.autoindent_mode = None;
1600 }
1601 }
1602
1603 pub fn set_read_only(&mut self, read_only: bool) {
1604 self.read_only = read_only;
1605 }
1606
1607 pub fn replica_id_map(&self) -> Option<&HashMap<ReplicaId, ReplicaId>> {
1608 self.replica_id_mapping.as_ref()
1609 }
1610
1611 pub fn set_replica_id_map(
1612 &mut self,
1613 mapping: Option<HashMap<ReplicaId, ReplicaId>>,
1614 cx: &mut ViewContext<Self>,
1615 ) {
1616 self.replica_id_mapping = mapping;
1617 cx.notify();
1618 }
1619
1620 fn selections_did_change(
1621 &mut self,
1622 local: bool,
1623 old_cursor_position: &Anchor,
1624 cx: &mut ViewContext<Self>,
1625 ) {
1626 if self.focused && self.leader_replica_id.is_none() {
1627 self.buffer.update(cx, |buffer, cx| {
1628 buffer.set_active_selections(
1629 &self.selections.disjoint_anchors(),
1630 self.selections.line_mode,
1631 self.cursor_shape,
1632 cx,
1633 )
1634 });
1635 }
1636
1637 let display_map = self
1638 .display_map
1639 .update(cx, |display_map, cx| display_map.snapshot(cx));
1640 let buffer = &display_map.buffer_snapshot;
1641 self.add_selections_state = None;
1642 self.select_next_state = None;
1643 self.select_prev_state = None;
1644 self.select_larger_syntax_node_stack.clear();
1645 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
1646 self.snippet_stack
1647 .invalidate(&self.selections.disjoint_anchors(), buffer);
1648 self.take_rename(false, cx);
1649
1650 let new_cursor_position = self.selections.newest_anchor().head();
1651
1652 self.push_to_nav_history(
1653 old_cursor_position.clone(),
1654 Some(new_cursor_position.to_point(buffer)),
1655 cx,
1656 );
1657
1658 if local {
1659 let new_cursor_position = self.selections.newest_anchor().head();
1660 let completion_menu = match self.context_menu.as_mut() {
1661 Some(ContextMenu::Completions(menu)) => Some(menu),
1662 _ => {
1663 self.context_menu.take();
1664 None
1665 }
1666 };
1667
1668 if let Some(completion_menu) = completion_menu {
1669 let cursor_position = new_cursor_position.to_offset(buffer);
1670 let (word_range, kind) =
1671 buffer.surrounding_word(completion_menu.initial_position.clone());
1672 if kind == Some(CharKind::Word)
1673 && word_range.to_inclusive().contains(&cursor_position)
1674 {
1675 let query = Self::completion_query(buffer, cursor_position);
1676 cx.background()
1677 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1678 self.show_completions(&ShowCompletions, cx);
1679 } else {
1680 self.hide_context_menu(cx);
1681 }
1682 }
1683
1684 hide_hover(self, cx);
1685
1686 if old_cursor_position.to_display_point(&display_map).row()
1687 != new_cursor_position.to_display_point(&display_map).row()
1688 {
1689 self.available_code_actions.take();
1690 }
1691 self.refresh_code_actions(cx);
1692 self.refresh_document_highlights(cx);
1693 refresh_matching_bracket_highlights(self, cx);
1694 self.discard_copilot_suggestion(cx);
1695 }
1696
1697 self.blink_manager.update(cx, BlinkManager::pause_blinking);
1698 cx.emit(Event::SelectionsChanged { local });
1699 cx.notify();
1700 }
1701
1702 pub fn change_selections<R>(
1703 &mut self,
1704 autoscroll: Option<Autoscroll>,
1705 cx: &mut ViewContext<Self>,
1706 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1707 ) -> R {
1708 let old_cursor_position = self.selections.newest_anchor().head();
1709 self.push_to_selection_history();
1710
1711 let (changed, result) = self.selections.change_with(cx, change);
1712
1713 if changed {
1714 if let Some(autoscroll) = autoscroll {
1715 self.request_autoscroll(autoscroll, cx);
1716 }
1717 self.selections_did_change(true, &old_cursor_position, cx);
1718 }
1719
1720 result
1721 }
1722
1723 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1724 where
1725 I: IntoIterator<Item = (Range<S>, T)>,
1726 S: ToOffset,
1727 T: Into<Arc<str>>,
1728 {
1729 if self.read_only {
1730 return;
1731 }
1732
1733 self.buffer
1734 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
1735 }
1736
1737 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1738 where
1739 I: IntoIterator<Item = (Range<S>, T)>,
1740 S: ToOffset,
1741 T: Into<Arc<str>>,
1742 {
1743 if self.read_only {
1744 return;
1745 }
1746
1747 self.buffer.update(cx, |buffer, cx| {
1748 buffer.edit(edits, self.autoindent_mode.clone(), cx)
1749 });
1750 }
1751
1752 pub fn edit_with_block_indent<I, S, T>(
1753 &mut self,
1754 edits: I,
1755 original_indent_columns: Vec<u32>,
1756 cx: &mut ViewContext<Self>,
1757 ) where
1758 I: IntoIterator<Item = (Range<S>, T)>,
1759 S: ToOffset,
1760 T: Into<Arc<str>>,
1761 {
1762 if self.read_only {
1763 return;
1764 }
1765
1766 self.buffer.update(cx, |buffer, cx| {
1767 buffer.edit(
1768 edits,
1769 Some(AutoindentMode::Block {
1770 original_indent_columns,
1771 }),
1772 cx,
1773 )
1774 });
1775 }
1776
1777 fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
1778 self.hide_context_menu(cx);
1779
1780 match phase {
1781 SelectPhase::Begin {
1782 position,
1783 add,
1784 click_count,
1785 } => self.begin_selection(position, add, click_count, cx),
1786 SelectPhase::BeginColumnar {
1787 position,
1788 goal_column,
1789 } => self.begin_columnar_selection(position, goal_column, cx),
1790 SelectPhase::Extend {
1791 position,
1792 click_count,
1793 } => self.extend_selection(position, click_count, cx),
1794 SelectPhase::Update {
1795 position,
1796 goal_column,
1797 scroll_position,
1798 } => self.update_selection(position, goal_column, scroll_position, cx),
1799 SelectPhase::End => self.end_selection(cx),
1800 }
1801 }
1802
1803 fn extend_selection(
1804 &mut self,
1805 position: DisplayPoint,
1806 click_count: usize,
1807 cx: &mut ViewContext<Self>,
1808 ) {
1809 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1810 let tail = self.selections.newest::<usize>(cx).tail();
1811 self.begin_selection(position, false, click_count, cx);
1812
1813 let position = position.to_offset(&display_map, Bias::Left);
1814 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1815
1816 let mut pending_selection = self
1817 .selections
1818 .pending_anchor()
1819 .expect("extend_selection not called with pending selection");
1820 if position >= tail {
1821 pending_selection.start = tail_anchor;
1822 } else {
1823 pending_selection.end = tail_anchor;
1824 pending_selection.reversed = true;
1825 }
1826
1827 let mut pending_mode = self.selections.pending_mode().unwrap();
1828 match &mut pending_mode {
1829 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
1830 _ => {}
1831 }
1832
1833 self.change_selections(Some(Autoscroll::fit()), cx, |s| {
1834 s.set_pending(pending_selection, pending_mode)
1835 });
1836 }
1837
1838 fn begin_selection(
1839 &mut self,
1840 position: DisplayPoint,
1841 add: bool,
1842 click_count: usize,
1843 cx: &mut ViewContext<Self>,
1844 ) {
1845 if !self.focused {
1846 cx.focus_self();
1847 }
1848
1849 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1850 let buffer = &display_map.buffer_snapshot;
1851 let newest_selection = self.selections.newest_anchor().clone();
1852 let position = display_map.clip_point(position, Bias::Left);
1853
1854 let start;
1855 let end;
1856 let mode;
1857 let auto_scroll;
1858 match click_count {
1859 1 => {
1860 start = buffer.anchor_before(position.to_point(&display_map));
1861 end = start.clone();
1862 mode = SelectMode::Character;
1863 auto_scroll = true;
1864 }
1865 2 => {
1866 let range = movement::surrounding_word(&display_map, position);
1867 start = buffer.anchor_before(range.start.to_point(&display_map));
1868 end = buffer.anchor_before(range.end.to_point(&display_map));
1869 mode = SelectMode::Word(start.clone()..end.clone());
1870 auto_scroll = true;
1871 }
1872 3 => {
1873 let position = display_map
1874 .clip_point(position, Bias::Left)
1875 .to_point(&display_map);
1876 let line_start = display_map.prev_line_boundary(position).0;
1877 let next_line_start = buffer.clip_point(
1878 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1879 Bias::Left,
1880 );
1881 start = buffer.anchor_before(line_start);
1882 end = buffer.anchor_before(next_line_start);
1883 mode = SelectMode::Line(start.clone()..end.clone());
1884 auto_scroll = true;
1885 }
1886 _ => {
1887 start = buffer.anchor_before(0);
1888 end = buffer.anchor_before(buffer.len());
1889 mode = SelectMode::All;
1890 auto_scroll = false;
1891 }
1892 }
1893
1894 self.change_selections(auto_scroll.then(|| Autoscroll::newest()), cx, |s| {
1895 if !add {
1896 s.clear_disjoint();
1897 } else if click_count > 1 {
1898 s.delete(newest_selection.id)
1899 }
1900
1901 s.set_pending_anchor_range(start..end, mode);
1902 });
1903 }
1904
1905 fn begin_columnar_selection(
1906 &mut self,
1907 position: DisplayPoint,
1908 goal_column: u32,
1909 cx: &mut ViewContext<Self>,
1910 ) {
1911 if !self.focused {
1912 cx.focus_self();
1913 }
1914
1915 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1916 let tail = self.selections.newest::<Point>(cx).tail();
1917 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1918
1919 self.select_columns(
1920 tail.to_display_point(&display_map),
1921 position,
1922 goal_column,
1923 &display_map,
1924 cx,
1925 );
1926 }
1927
1928 fn update_selection(
1929 &mut self,
1930 position: DisplayPoint,
1931 goal_column: u32,
1932 scroll_position: Vector2F,
1933 cx: &mut ViewContext<Self>,
1934 ) {
1935 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1936
1937 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1938 let tail = tail.to_display_point(&display_map);
1939 self.select_columns(tail, position, goal_column, &display_map, cx);
1940 } else if let Some(mut pending) = self.selections.pending_anchor() {
1941 let buffer = self.buffer.read(cx).snapshot(cx);
1942 let head;
1943 let tail;
1944 let mode = self.selections.pending_mode().unwrap();
1945 match &mode {
1946 SelectMode::Character => {
1947 head = position.to_point(&display_map);
1948 tail = pending.tail().to_point(&buffer);
1949 }
1950 SelectMode::Word(original_range) => {
1951 let original_display_range = original_range.start.to_display_point(&display_map)
1952 ..original_range.end.to_display_point(&display_map);
1953 let original_buffer_range = original_display_range.start.to_point(&display_map)
1954 ..original_display_range.end.to_point(&display_map);
1955 if movement::is_inside_word(&display_map, position)
1956 || original_display_range.contains(&position)
1957 {
1958 let word_range = movement::surrounding_word(&display_map, position);
1959 if word_range.start < original_display_range.start {
1960 head = word_range.start.to_point(&display_map);
1961 } else {
1962 head = word_range.end.to_point(&display_map);
1963 }
1964 } else {
1965 head = position.to_point(&display_map);
1966 }
1967
1968 if head <= original_buffer_range.start {
1969 tail = original_buffer_range.end;
1970 } else {
1971 tail = original_buffer_range.start;
1972 }
1973 }
1974 SelectMode::Line(original_range) => {
1975 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1976
1977 let position = display_map
1978 .clip_point(position, Bias::Left)
1979 .to_point(&display_map);
1980 let line_start = display_map.prev_line_boundary(position).0;
1981 let next_line_start = buffer.clip_point(
1982 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1983 Bias::Left,
1984 );
1985
1986 if line_start < original_range.start {
1987 head = line_start
1988 } else {
1989 head = next_line_start
1990 }
1991
1992 if head <= original_range.start {
1993 tail = original_range.end;
1994 } else {
1995 tail = original_range.start;
1996 }
1997 }
1998 SelectMode::All => {
1999 return;
2000 }
2001 };
2002
2003 if head < tail {
2004 pending.start = buffer.anchor_before(head);
2005 pending.end = buffer.anchor_before(tail);
2006 pending.reversed = true;
2007 } else {
2008 pending.start = buffer.anchor_before(tail);
2009 pending.end = buffer.anchor_before(head);
2010 pending.reversed = false;
2011 }
2012
2013 self.change_selections(None, cx, |s| {
2014 s.set_pending(pending, mode);
2015 });
2016 } else {
2017 error!("update_selection dispatched with no pending selection");
2018 return;
2019 }
2020
2021 self.set_scroll_position(scroll_position, cx);
2022 cx.notify();
2023 }
2024
2025 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
2026 self.columnar_selection_tail.take();
2027 if self.selections.pending_anchor().is_some() {
2028 let selections = self.selections.all::<usize>(cx);
2029 self.change_selections(None, cx, |s| {
2030 s.select(selections);
2031 s.clear_pending();
2032 });
2033 }
2034 }
2035
2036 fn select_columns(
2037 &mut self,
2038 tail: DisplayPoint,
2039 head: DisplayPoint,
2040 goal_column: u32,
2041 display_map: &DisplaySnapshot,
2042 cx: &mut ViewContext<Self>,
2043 ) {
2044 let start_row = cmp::min(tail.row(), head.row());
2045 let end_row = cmp::max(tail.row(), head.row());
2046 let start_column = cmp::min(tail.column(), goal_column);
2047 let end_column = cmp::max(tail.column(), goal_column);
2048 let reversed = start_column < tail.column();
2049
2050 let selection_ranges = (start_row..=end_row)
2051 .filter_map(|row| {
2052 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2053 let start = display_map
2054 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2055 .to_point(display_map);
2056 let end = display_map
2057 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2058 .to_point(display_map);
2059 if reversed {
2060 Some(end..start)
2061 } else {
2062 Some(start..end)
2063 }
2064 } else {
2065 None
2066 }
2067 })
2068 .collect::<Vec<_>>();
2069
2070 self.change_selections(None, cx, |s| {
2071 s.select_ranges(selection_ranges);
2072 });
2073 cx.notify();
2074 }
2075
2076 pub fn has_pending_nonempty_selection(&self) -> bool {
2077 let pending_nonempty_selection = match self.selections.pending_anchor() {
2078 Some(Selection { start, end, .. }) => start != end,
2079 None => false,
2080 };
2081 pending_nonempty_selection || self.columnar_selection_tail.is_some()
2082 }
2083
2084 pub fn has_pending_selection(&self) -> bool {
2085 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2086 }
2087
2088 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
2089 if self.take_rename(false, cx).is_some() {
2090 return;
2091 }
2092
2093 if hide_hover(self, cx) {
2094 return;
2095 }
2096
2097 if self.hide_context_menu(cx).is_some() {
2098 return;
2099 }
2100
2101 if self.discard_copilot_suggestion(cx) {
2102 return;
2103 }
2104
2105 if self.snippet_stack.pop().is_some() {
2106 return;
2107 }
2108
2109 if self.mode == EditorMode::Full {
2110 if self.active_diagnostics.is_some() {
2111 self.dismiss_diagnostics(cx);
2112 return;
2113 }
2114
2115 if self.change_selections(Some(Autoscroll::fit()), cx, |s| s.try_cancel()) {
2116 return;
2117 }
2118 }
2119
2120 cx.propagate_action();
2121 }
2122
2123 pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2124 let text: Arc<str> = text.into();
2125
2126 if self.read_only {
2127 return;
2128 }
2129 if !self.input_enabled {
2130 cx.emit(Event::InputIgnored { text });
2131 return;
2132 }
2133
2134 let selections = self.selections.all_adjusted(cx);
2135 let mut brace_inserted = false;
2136 let mut edits = Vec::new();
2137 let mut new_selections = Vec::with_capacity(selections.len());
2138 let mut new_autoclose_regions = Vec::new();
2139 let snapshot = self.buffer.read(cx).read(cx);
2140
2141 for (selection, autoclose_region) in
2142 self.selections_with_autoclose_regions(selections, &snapshot)
2143 {
2144 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2145 // Determine if the inserted text matches the opening or closing
2146 // bracket of any of this language's bracket pairs.
2147 let mut bracket_pair = None;
2148 let mut is_bracket_pair_start = false;
2149 for (pair, enabled) in scope.brackets() {
2150 if enabled && pair.close && pair.start.ends_with(text.as_ref()) {
2151 bracket_pair = Some(pair.clone());
2152 is_bracket_pair_start = true;
2153 break;
2154 } else if pair.end.as_str() == text.as_ref() {
2155 bracket_pair = Some(pair.clone());
2156 break;
2157 }
2158 }
2159
2160 if let Some(bracket_pair) = bracket_pair {
2161 if selection.is_empty() {
2162 if is_bracket_pair_start {
2163 let prefix_len = bracket_pair.start.len() - text.len();
2164
2165 // If the inserted text is a suffix of an opening bracket and the
2166 // selection is preceded by the rest of the opening bracket, then
2167 // insert the closing bracket.
2168 let following_text_allows_autoclose = snapshot
2169 .chars_at(selection.start)
2170 .next()
2171 .map_or(true, |c| scope.should_autoclose_before(c));
2172 let preceding_text_matches_prefix = prefix_len == 0
2173 || (selection.start.column >= (prefix_len as u32)
2174 && snapshot.contains_str_at(
2175 Point::new(
2176 selection.start.row,
2177 selection.start.column - (prefix_len as u32),
2178 ),
2179 &bracket_pair.start[..prefix_len],
2180 ));
2181 if following_text_allows_autoclose && preceding_text_matches_prefix {
2182 let anchor = snapshot.anchor_before(selection.end);
2183 new_selections.push((selection.map(|_| anchor), text.len()));
2184 new_autoclose_regions.push((
2185 anchor,
2186 text.len(),
2187 selection.id,
2188 bracket_pair.clone(),
2189 ));
2190 edits.push((
2191 selection.range(),
2192 format!("{}{}", text, bracket_pair.end).into(),
2193 ));
2194 brace_inserted = true;
2195 continue;
2196 }
2197 }
2198
2199 if let Some(region) = autoclose_region {
2200 // If the selection is followed by an auto-inserted closing bracket,
2201 // then don't insert that closing bracket again; just move the selection
2202 // past the closing bracket.
2203 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2204 && text.as_ref() == region.pair.end.as_str();
2205 if should_skip {
2206 let anchor = snapshot.anchor_after(selection.end);
2207 new_selections
2208 .push((selection.map(|_| anchor), region.pair.end.len()));
2209 continue;
2210 }
2211 }
2212 }
2213 // If an opening bracket is 1 character long and is typed while
2214 // text is selected, then surround that text with the bracket pair.
2215 else if is_bracket_pair_start && bracket_pair.start.chars().count() == 1 {
2216 edits.push((selection.start..selection.start, text.clone()));
2217 edits.push((
2218 selection.end..selection.end,
2219 bracket_pair.end.as_str().into(),
2220 ));
2221 brace_inserted = true;
2222 new_selections.push((
2223 Selection {
2224 id: selection.id,
2225 start: snapshot.anchor_after(selection.start),
2226 end: snapshot.anchor_before(selection.end),
2227 reversed: selection.reversed,
2228 goal: selection.goal,
2229 },
2230 0,
2231 ));
2232 continue;
2233 }
2234 }
2235 }
2236
2237 // If not handling any auto-close operation, then just replace the selected
2238 // text with the given input and move the selection to the end of the
2239 // newly inserted text.
2240 let anchor = snapshot.anchor_after(selection.end);
2241 new_selections.push((selection.map(|_| anchor), 0));
2242 edits.push((selection.start..selection.end, text.clone()));
2243 }
2244
2245 drop(snapshot);
2246 self.transact(cx, |this, cx| {
2247 this.buffer.update(cx, |buffer, cx| {
2248 buffer.edit(edits, this.autoindent_mode.clone(), cx);
2249 });
2250
2251 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
2252 let new_selection_deltas = new_selections.iter().map(|e| e.1);
2253 let snapshot = this.buffer.read(cx).read(cx);
2254 let new_selections = resolve_multiple::<usize, _>(new_anchor_selections, &snapshot)
2255 .zip(new_selection_deltas)
2256 .map(|(selection, delta)| selection.map(|e| e + delta))
2257 .collect::<Vec<_>>();
2258
2259 let mut i = 0;
2260 for (position, delta, selection_id, pair) in new_autoclose_regions {
2261 let position = position.to_offset(&snapshot) + delta;
2262 let start = snapshot.anchor_before(position);
2263 let end = snapshot.anchor_after(position);
2264 while let Some(existing_state) = this.autoclose_regions.get(i) {
2265 match existing_state.range.start.cmp(&start, &snapshot) {
2266 Ordering::Less => i += 1,
2267 Ordering::Greater => break,
2268 Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) {
2269 Ordering::Less => i += 1,
2270 Ordering::Equal => break,
2271 Ordering::Greater => break,
2272 },
2273 }
2274 }
2275 this.autoclose_regions.insert(
2276 i,
2277 AutocloseRegion {
2278 selection_id,
2279 range: start..end,
2280 pair,
2281 },
2282 );
2283 }
2284
2285 drop(snapshot);
2286 let had_active_copilot_suggestion = this.has_active_copilot_suggestion(cx);
2287 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2288
2289 if !brace_inserted && settings::get::<EditorSettings>(cx).use_on_type_format {
2290 if let Some(on_type_format_task) =
2291 this.trigger_on_type_formatting(text.to_string(), cx)
2292 {
2293 on_type_format_task.detach_and_log_err(cx);
2294 }
2295 }
2296
2297 if had_active_copilot_suggestion {
2298 this.refresh_copilot_suggestions(true, cx);
2299 if !this.has_active_copilot_suggestion(cx) {
2300 this.trigger_completion_on_input(&text, cx);
2301 }
2302 } else {
2303 this.trigger_completion_on_input(&text, cx);
2304 this.refresh_copilot_suggestions(true, cx);
2305 }
2306 });
2307 }
2308
2309 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
2310 self.transact(cx, |this, cx| {
2311 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
2312 let selections = this.selections.all::<usize>(cx);
2313 let multi_buffer = this.buffer.read(cx);
2314 let buffer = multi_buffer.snapshot(cx);
2315 selections
2316 .iter()
2317 .map(|selection| {
2318 let start_point = selection.start.to_point(&buffer);
2319 let mut indent = buffer.indent_size_for_line(start_point.row);
2320 indent.len = cmp::min(indent.len, start_point.column);
2321 let start = selection.start;
2322 let end = selection.end;
2323 let is_cursor = start == end;
2324 let language_scope = buffer.language_scope_at(start);
2325 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
2326 &language_scope
2327 {
2328 let leading_whitespace_len = buffer
2329 .reversed_chars_at(start)
2330 .take_while(|c| c.is_whitespace() && *c != '\n')
2331 .map(|c| c.len_utf8())
2332 .sum::<usize>();
2333
2334 let trailing_whitespace_len = buffer
2335 .chars_at(end)
2336 .take_while(|c| c.is_whitespace() && *c != '\n')
2337 .map(|c| c.len_utf8())
2338 .sum::<usize>();
2339
2340 let insert_extra_newline =
2341 language.brackets().any(|(pair, enabled)| {
2342 let pair_start = pair.start.trim_end();
2343 let pair_end = pair.end.trim_start();
2344
2345 enabled
2346 && pair.newline
2347 && buffer.contains_str_at(
2348 end + trailing_whitespace_len,
2349 pair_end,
2350 )
2351 && buffer.contains_str_at(
2352 (start - leading_whitespace_len)
2353 .saturating_sub(pair_start.len()),
2354 pair_start,
2355 )
2356 });
2357 // Comment extension on newline is allowed only for cursor selections
2358 let comment_delimiter = language.line_comment_prefix().filter(|_| {
2359 let is_comment_extension_enabled =
2360 multi_buffer.settings_at(0, cx).extend_comment_on_newline;
2361 is_cursor && is_comment_extension_enabled
2362 });
2363 let comment_delimiter = if let Some(delimiter) = comment_delimiter {
2364 buffer
2365 .buffer_line_for_row(start_point.row)
2366 .is_some_and(|(snapshot, range)| {
2367 let mut index_of_first_non_whitespace = 0;
2368 let line_starts_with_comment = snapshot
2369 .chars_for_range(range)
2370 .skip_while(|c| {
2371 let should_skip = c.is_whitespace();
2372 if should_skip {
2373 index_of_first_non_whitespace += 1;
2374 }
2375 should_skip
2376 })
2377 .take(delimiter.len())
2378 .eq(delimiter.chars());
2379 let cursor_is_placed_after_comment_marker =
2380 index_of_first_non_whitespace + delimiter.len()
2381 <= start_point.column as usize;
2382 line_starts_with_comment
2383 && cursor_is_placed_after_comment_marker
2384 })
2385 .then(|| delimiter.clone())
2386 } else {
2387 None
2388 };
2389 (comment_delimiter, insert_extra_newline)
2390 } else {
2391 (None, false)
2392 };
2393
2394 let capacity_for_delimiter = comment_delimiter
2395 .as_deref()
2396 .map(str::len)
2397 .unwrap_or_default();
2398 let mut new_text =
2399 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
2400 new_text.push_str("\n");
2401 new_text.extend(indent.chars());
2402 if let Some(delimiter) = &comment_delimiter {
2403 new_text.push_str(&delimiter);
2404 }
2405 if insert_extra_newline {
2406 new_text = new_text.repeat(2);
2407 }
2408
2409 let anchor = buffer.anchor_after(end);
2410 let new_selection = selection.map(|_| anchor);
2411 (
2412 (start..end, new_text),
2413 (insert_extra_newline, new_selection),
2414 )
2415 })
2416 .unzip()
2417 };
2418
2419 this.edit_with_autoindent(edits, cx);
2420 let buffer = this.buffer.read(cx).snapshot(cx);
2421 let new_selections = selection_fixup_info
2422 .into_iter()
2423 .map(|(extra_newline_inserted, new_selection)| {
2424 let mut cursor = new_selection.end.to_point(&buffer);
2425 if extra_newline_inserted {
2426 cursor.row -= 1;
2427 cursor.column = buffer.line_len(cursor.row);
2428 }
2429 new_selection.map(|_| cursor)
2430 })
2431 .collect();
2432
2433 this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(new_selections));
2434 this.refresh_copilot_suggestions(true, cx);
2435 });
2436 }
2437
2438 pub fn newline_above(&mut self, _: &NewlineAbove, cx: &mut ViewContext<Self>) {
2439 let buffer = self.buffer.read(cx);
2440 let snapshot = buffer.snapshot(cx);
2441
2442 let mut edits = Vec::new();
2443 let mut rows = Vec::new();
2444 let mut rows_inserted = 0;
2445
2446 for selection in self.selections.all_adjusted(cx) {
2447 let cursor = selection.head();
2448 let row = cursor.row;
2449
2450 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
2451
2452 let newline = "\n".to_string();
2453 edits.push((start_of_line..start_of_line, newline));
2454
2455 rows.push(row + rows_inserted);
2456 rows_inserted += 1;
2457 }
2458
2459 self.transact(cx, |editor, cx| {
2460 editor.edit(edits, cx);
2461
2462 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2463 let mut index = 0;
2464 s.move_cursors_with(|map, _, _| {
2465 let row = rows[index];
2466 index += 1;
2467
2468 let point = Point::new(row, 0);
2469 let boundary = map.next_line_boundary(point).1;
2470 let clipped = map.clip_point(boundary, Bias::Left);
2471
2472 (clipped, SelectionGoal::None)
2473 });
2474 });
2475
2476 let mut indent_edits = Vec::new();
2477 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2478 for row in rows {
2479 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2480 for (row, indent) in indents {
2481 if indent.len == 0 {
2482 continue;
2483 }
2484
2485 let text = match indent.kind {
2486 IndentKind::Space => " ".repeat(indent.len as usize),
2487 IndentKind::Tab => "\t".repeat(indent.len as usize),
2488 };
2489 let point = Point::new(row, 0);
2490 indent_edits.push((point..point, text));
2491 }
2492 }
2493 editor.edit(indent_edits, cx);
2494 });
2495 }
2496
2497 pub fn newline_below(&mut self, _: &NewlineBelow, cx: &mut ViewContext<Self>) {
2498 let buffer = self.buffer.read(cx);
2499 let snapshot = buffer.snapshot(cx);
2500
2501 let mut edits = Vec::new();
2502 let mut rows = Vec::new();
2503 let mut rows_inserted = 0;
2504
2505 for selection in self.selections.all_adjusted(cx) {
2506 let cursor = selection.head();
2507 let row = cursor.row;
2508
2509 let point = Point::new(row + 1, 0);
2510 let start_of_line = snapshot.clip_point(point, Bias::Left);
2511
2512 let newline = "\n".to_string();
2513 edits.push((start_of_line..start_of_line, newline));
2514
2515 rows_inserted += 1;
2516 rows.push(row + rows_inserted);
2517 }
2518
2519 self.transact(cx, |editor, cx| {
2520 editor.edit(edits, cx);
2521
2522 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
2523 let mut index = 0;
2524 s.move_cursors_with(|map, _, _| {
2525 let row = rows[index];
2526 index += 1;
2527
2528 let point = Point::new(row, 0);
2529 let boundary = map.next_line_boundary(point).1;
2530 let clipped = map.clip_point(boundary, Bias::Left);
2531
2532 (clipped, SelectionGoal::None)
2533 });
2534 });
2535
2536 let mut indent_edits = Vec::new();
2537 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
2538 for row in rows {
2539 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
2540 for (row, indent) in indents {
2541 if indent.len == 0 {
2542 continue;
2543 }
2544
2545 let text = match indent.kind {
2546 IndentKind::Space => " ".repeat(indent.len as usize),
2547 IndentKind::Tab => "\t".repeat(indent.len as usize),
2548 };
2549 let point = Point::new(row, 0);
2550 indent_edits.push((point..point, text));
2551 }
2552 }
2553 editor.edit(indent_edits, cx);
2554 });
2555 }
2556
2557 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2558 self.insert_with_autoindent_mode(
2559 text,
2560 Some(AutoindentMode::Block {
2561 original_indent_columns: Vec::new(),
2562 }),
2563 cx,
2564 );
2565 }
2566
2567 fn insert_with_autoindent_mode(
2568 &mut self,
2569 text: &str,
2570 autoindent_mode: Option<AutoindentMode>,
2571 cx: &mut ViewContext<Self>,
2572 ) {
2573 if self.read_only {
2574 return;
2575 }
2576
2577 let text: Arc<str> = text.into();
2578 self.transact(cx, |this, cx| {
2579 let old_selections = this.selections.all_adjusted(cx);
2580 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
2581 let anchors = {
2582 let snapshot = buffer.read(cx);
2583 old_selections
2584 .iter()
2585 .map(|s| {
2586 let anchor = snapshot.anchor_after(s.head());
2587 s.map(|_| anchor)
2588 })
2589 .collect::<Vec<_>>()
2590 };
2591 buffer.edit(
2592 old_selections
2593 .iter()
2594 .map(|s| (s.start..s.end, text.clone())),
2595 autoindent_mode,
2596 cx,
2597 );
2598 anchors
2599 });
2600
2601 this.change_selections(Some(Autoscroll::fit()), cx, |s| {
2602 s.select_anchors(selection_anchors);
2603 })
2604 });
2605 }
2606
2607 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
2608 if !settings::get::<EditorSettings>(cx).show_completions_on_input {
2609 return;
2610 }
2611
2612 let selection = self.selections.newest_anchor();
2613 if self
2614 .buffer
2615 .read(cx)
2616 .is_completion_trigger(selection.head(), text, cx)
2617 {
2618 self.show_completions(&ShowCompletions, cx);
2619 } else {
2620 self.hide_context_menu(cx);
2621 }
2622 }
2623
2624 /// If any empty selections is touching the start of its innermost containing autoclose
2625 /// region, expand it to select the brackets.
2626 fn select_autoclose_pair(&mut self, cx: &mut ViewContext<Self>) {
2627 let selections = self.selections.all::<usize>(cx);
2628 let buffer = self.buffer.read(cx).read(cx);
2629 let mut new_selections = Vec::new();
2630 for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) {
2631 if let (Some(region), true) = (region, selection.is_empty()) {
2632 let mut range = region.range.to_offset(&buffer);
2633 if selection.start == range.start {
2634 if range.start >= region.pair.start.len() {
2635 range.start -= region.pair.start.len();
2636 if buffer.contains_str_at(range.start, ®ion.pair.start) {
2637 if buffer.contains_str_at(range.end, ®ion.pair.end) {
2638 range.end += region.pair.end.len();
2639 selection.start = range.start;
2640 selection.end = range.end;
2641 }
2642 }
2643 }
2644 }
2645 }
2646 new_selections.push(selection);
2647 }
2648
2649 drop(buffer);
2650 self.change_selections(None, cx, |selections| selections.select(new_selections));
2651 }
2652
2653 /// Iterate the given selections, and for each one, find the smallest surrounding
2654 /// autoclose region. This uses the ordering of the selections and the autoclose
2655 /// regions to avoid repeated comparisons.
2656 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
2657 &'a self,
2658 selections: impl IntoIterator<Item = Selection<D>>,
2659 buffer: &'a MultiBufferSnapshot,
2660 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
2661 let mut i = 0;
2662 let mut regions = self.autoclose_regions.as_slice();
2663 selections.into_iter().map(move |selection| {
2664 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
2665
2666 let mut enclosing = None;
2667 while let Some(pair_state) = regions.get(i) {
2668 if pair_state.range.end.to_offset(buffer) < range.start {
2669 regions = ®ions[i + 1..];
2670 i = 0;
2671 } else if pair_state.range.start.to_offset(buffer) > range.end {
2672 break;
2673 } else if pair_state.selection_id == selection.id {
2674 enclosing = Some(pair_state);
2675 i += 1;
2676 }
2677 }
2678
2679 (selection.clone(), enclosing)
2680 })
2681 }
2682
2683 /// Remove any autoclose regions that no longer contain their selection.
2684 fn invalidate_autoclose_regions(
2685 &mut self,
2686 mut selections: &[Selection<Anchor>],
2687 buffer: &MultiBufferSnapshot,
2688 ) {
2689 self.autoclose_regions.retain(|state| {
2690 let mut i = 0;
2691 while let Some(selection) = selections.get(i) {
2692 if selection.end.cmp(&state.range.start, buffer).is_lt() {
2693 selections = &selections[1..];
2694 continue;
2695 }
2696 if selection.start.cmp(&state.range.end, buffer).is_gt() {
2697 break;
2698 }
2699 if selection.id == state.selection_id {
2700 return true;
2701 } else {
2702 i += 1;
2703 }
2704 }
2705 false
2706 });
2707 }
2708 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2709 let offset = position.to_offset(buffer);
2710 let (word_range, kind) = buffer.surrounding_word(offset);
2711 if offset > word_range.start && kind == Some(CharKind::Word) {
2712 Some(
2713 buffer
2714 .text_for_range(word_range.start..offset)
2715 .collect::<String>(),
2716 )
2717 } else {
2718 None
2719 }
2720 }
2721
2722 pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
2723 self.refresh_inlay_hints(
2724 InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled),
2725 cx,
2726 );
2727 }
2728
2729 pub fn inlay_hints_enabled(&self) -> bool {
2730 self.inlay_hint_cache.enabled
2731 }
2732
2733 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
2734 if self.project.is_none() || self.mode != EditorMode::Full {
2735 return;
2736 }
2737
2738 let (invalidate_cache, required_languages) = match reason {
2739 InlayHintRefreshReason::Toggle(enabled) => {
2740 self.inlay_hint_cache.enabled = enabled;
2741 if enabled {
2742 (InvalidationStrategy::RefreshRequested, None)
2743 } else {
2744 self.inlay_hint_cache.clear();
2745 self.splice_inlay_hints(
2746 self.visible_inlay_hints(cx)
2747 .iter()
2748 .map(|inlay| inlay.id)
2749 .collect(),
2750 Vec::new(),
2751 cx,
2752 );
2753 return;
2754 }
2755 }
2756 InlayHintRefreshReason::SettingsChange(new_settings) => {
2757 match self.inlay_hint_cache.update_settings(
2758 &self.buffer,
2759 new_settings,
2760 self.visible_inlay_hints(cx),
2761 cx,
2762 ) {
2763 ControlFlow::Break(Some(InlaySplice {
2764 to_remove,
2765 to_insert,
2766 })) => {
2767 self.splice_inlay_hints(to_remove, to_insert, cx);
2768 return;
2769 }
2770 ControlFlow::Break(None) => return,
2771 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
2772 }
2773 }
2774 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
2775 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
2776 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
2777 }
2778 InlayHintRefreshReason::RefreshRequested => {
2779 (InvalidationStrategy::RefreshRequested, None)
2780 }
2781 };
2782
2783 if let Some(InlaySplice {
2784 to_remove,
2785 to_insert,
2786 }) = self.inlay_hint_cache.spawn_hint_refresh(
2787 self.excerpt_visible_offsets(required_languages.as_ref(), cx),
2788 invalidate_cache,
2789 cx,
2790 ) {
2791 self.splice_inlay_hints(to_remove, to_insert, cx);
2792 }
2793 }
2794
2795 fn visible_inlay_hints(&self, cx: &ViewContext<'_, '_, Editor>) -> Vec<Inlay> {
2796 self.display_map
2797 .read(cx)
2798 .current_inlays()
2799 .filter(move |inlay| {
2800 Some(inlay.id) != self.copilot_state.suggestion.as_ref().map(|h| h.id)
2801 })
2802 .cloned()
2803 .collect()
2804 }
2805
2806 pub fn excerpt_visible_offsets(
2807 &self,
2808 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
2809 cx: &mut ViewContext<'_, '_, Editor>,
2810 ) -> HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)> {
2811 let multi_buffer = self.buffer().read(cx);
2812 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2813 let multi_buffer_visible_start = self
2814 .scroll_manager
2815 .anchor()
2816 .anchor
2817 .to_point(&multi_buffer_snapshot);
2818 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
2819 multi_buffer_visible_start
2820 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
2821 Bias::Left,
2822 );
2823 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
2824 multi_buffer
2825 .range_to_buffer_ranges(multi_buffer_visible_range, cx)
2826 .into_iter()
2827 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
2828 .filter_map(|(buffer_handle, excerpt_visible_range, excerpt_id)| {
2829 let buffer = buffer_handle.read(cx);
2830 let language = buffer.language()?;
2831 if let Some(restrict_to_languages) = restrict_to_languages {
2832 if !restrict_to_languages.contains(language) {
2833 return None;
2834 }
2835 }
2836 Some((
2837 excerpt_id,
2838 (
2839 buffer_handle,
2840 buffer.version().clone(),
2841 excerpt_visible_range,
2842 ),
2843 ))
2844 })
2845 .collect()
2846 }
2847
2848 fn splice_inlay_hints(
2849 &self,
2850 to_remove: Vec<InlayId>,
2851 to_insert: Vec<Inlay>,
2852 cx: &mut ViewContext<Self>,
2853 ) {
2854 self.display_map.update(cx, |display_map, cx| {
2855 display_map.splice_inlays(to_remove, to_insert, cx);
2856 });
2857 cx.notify();
2858 }
2859
2860 fn trigger_on_type_formatting(
2861 &self,
2862 input: String,
2863 cx: &mut ViewContext<Self>,
2864 ) -> Option<Task<Result<()>>> {
2865 if input.len() != 1 {
2866 return None;
2867 }
2868
2869 let project = self.project.as_ref()?;
2870 let position = self.selections.newest_anchor().head();
2871 let (buffer, buffer_position) = self
2872 .buffer
2873 .read(cx)
2874 .text_anchor_for_position(position.clone(), cx)?;
2875
2876 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
2877 // hence we do LSP request & edit on host side only — add formats to host's history.
2878 let push_to_lsp_host_history = true;
2879 // If this is not the host, append its history with new edits.
2880 let push_to_client_history = project.read(cx).is_remote();
2881
2882 let on_type_formatting = project.update(cx, |project, cx| {
2883 project.on_type_format(
2884 buffer.clone(),
2885 buffer_position,
2886 input,
2887 push_to_lsp_host_history,
2888 cx,
2889 )
2890 });
2891 Some(cx.spawn(|editor, mut cx| async move {
2892 if let Some(transaction) = on_type_formatting.await? {
2893 if push_to_client_history {
2894 buffer.update(&mut cx, |buffer, _| {
2895 buffer.push_transaction(transaction, Instant::now());
2896 });
2897 }
2898 editor.update(&mut cx, |editor, cx| {
2899 editor.refresh_document_highlights(cx);
2900 })?;
2901 }
2902 Ok(())
2903 }))
2904 }
2905
2906 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2907 if self.pending_rename.is_some() {
2908 return;
2909 }
2910
2911 let project = if let Some(project) = self.project.clone() {
2912 project
2913 } else {
2914 return;
2915 };
2916
2917 let position = self.selections.newest_anchor().head();
2918 let (buffer, buffer_position) = if let Some(output) = self
2919 .buffer
2920 .read(cx)
2921 .text_anchor_for_position(position.clone(), cx)
2922 {
2923 output
2924 } else {
2925 return;
2926 };
2927
2928 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2929 let completions = project.update(cx, |project, cx| {
2930 project.completions(&buffer, buffer_position, cx)
2931 });
2932
2933 let id = post_inc(&mut self.next_completion_id);
2934 let task = cx.spawn(|this, mut cx| {
2935 async move {
2936 let menu = if let Some(completions) = completions.await.log_err() {
2937 let mut menu = CompletionsMenu {
2938 id,
2939 initial_position: position,
2940 match_candidates: completions
2941 .iter()
2942 .enumerate()
2943 .map(|(id, completion)| {
2944 StringMatchCandidate::new(
2945 id,
2946 completion.label.text[completion.label.filter_range.clone()]
2947 .into(),
2948 )
2949 })
2950 .collect(),
2951 buffer,
2952 completions: completions.into(),
2953 matches: Vec::new().into(),
2954 selected_item: 0,
2955 list: Default::default(),
2956 };
2957 menu.filter(query.as_deref(), cx.background()).await;
2958 if menu.matches.is_empty() {
2959 None
2960 } else {
2961 Some(menu)
2962 }
2963 } else {
2964 None
2965 };
2966
2967 this.update(&mut cx, |this, cx| {
2968 this.completion_tasks.retain(|(task_id, _)| *task_id > id);
2969
2970 match this.context_menu.as_ref() {
2971 None => {}
2972 Some(ContextMenu::Completions(prev_menu)) => {
2973 if prev_menu.id > id {
2974 return;
2975 }
2976 }
2977 _ => return,
2978 }
2979
2980 if this.focused && menu.is_some() {
2981 let menu = menu.unwrap();
2982 this.show_context_menu(ContextMenu::Completions(menu), cx);
2983 } else if this.completion_tasks.is_empty() {
2984 // If there are no more completion tasks and the last menu was
2985 // empty, we should hide it. If it was already hidden, we should
2986 // also show the copilot suggestion when available.
2987 if this.hide_context_menu(cx).is_none() {
2988 this.update_visible_copilot_suggestion(cx);
2989 }
2990 }
2991 })?;
2992
2993 Ok::<_, anyhow::Error>(())
2994 }
2995 .log_err()
2996 });
2997 self.completion_tasks.push((id, task));
2998 }
2999
3000 pub fn confirm_completion(
3001 &mut self,
3002 action: &ConfirmCompletion,
3003 cx: &mut ViewContext<Self>,
3004 ) -> Option<Task<Result<()>>> {
3005 use language::ToOffset as _;
3006
3007 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
3008 menu
3009 } else {
3010 return None;
3011 };
3012
3013 let mat = completions_menu
3014 .matches
3015 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
3016 let buffer_handle = completions_menu.buffer;
3017 let completion = completions_menu.completions.get(mat.candidate_id)?;
3018
3019 let snippet;
3020 let text;
3021 if completion.is_snippet() {
3022 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
3023 text = snippet.as_ref().unwrap().text.clone();
3024 } else {
3025 snippet = None;
3026 text = completion.new_text.clone();
3027 };
3028 let selections = self.selections.all::<usize>(cx);
3029 let buffer = buffer_handle.read(cx);
3030 let old_range = completion.old_range.to_offset(buffer);
3031 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
3032
3033 let newest_selection = self.selections.newest_anchor();
3034 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
3035 return None;
3036 }
3037
3038 let lookbehind = newest_selection
3039 .start
3040 .text_anchor
3041 .to_offset(buffer)
3042 .saturating_sub(old_range.start);
3043 let lookahead = old_range
3044 .end
3045 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
3046 let mut common_prefix_len = old_text
3047 .bytes()
3048 .zip(text.bytes())
3049 .take_while(|(a, b)| a == b)
3050 .count();
3051
3052 let snapshot = self.buffer.read(cx).snapshot(cx);
3053 let mut ranges = Vec::new();
3054 for selection in &selections {
3055 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
3056 let start = selection.start.saturating_sub(lookbehind);
3057 let end = selection.end + lookahead;
3058 ranges.push(start + common_prefix_len..end);
3059 } else {
3060 common_prefix_len = 0;
3061 ranges.clear();
3062 ranges.extend(selections.iter().map(|s| {
3063 if s.id == newest_selection.id {
3064 old_range.clone()
3065 } else {
3066 s.start..s.end
3067 }
3068 }));
3069 break;
3070 }
3071 }
3072 let text = &text[common_prefix_len..];
3073
3074 self.transact(cx, |this, cx| {
3075 if let Some(mut snippet) = snippet {
3076 snippet.text = text.to_string();
3077 for tabstop in snippet.tabstops.iter_mut().flatten() {
3078 tabstop.start -= common_prefix_len as isize;
3079 tabstop.end -= common_prefix_len as isize;
3080 }
3081
3082 this.insert_snippet(&ranges, snippet, cx).log_err();
3083 } else {
3084 this.buffer.update(cx, |buffer, cx| {
3085 buffer.edit(
3086 ranges.iter().map(|range| (range.clone(), text)),
3087 this.autoindent_mode.clone(),
3088 cx,
3089 );
3090 });
3091 }
3092
3093 this.refresh_copilot_suggestions(true, cx);
3094 });
3095
3096 let project = self.project.clone()?;
3097 let apply_edits = project.update(cx, |project, cx| {
3098 project.apply_additional_edits_for_completion(
3099 buffer_handle,
3100 completion.clone(),
3101 true,
3102 cx,
3103 )
3104 });
3105 Some(cx.foreground().spawn(async move {
3106 apply_edits.await?;
3107 Ok(())
3108 }))
3109 }
3110
3111 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
3112 if matches!(
3113 self.context_menu.as_ref(),
3114 Some(ContextMenu::CodeActions(_))
3115 ) {
3116 self.context_menu.take();
3117 cx.notify();
3118 return;
3119 }
3120
3121 let deployed_from_indicator = action.deployed_from_indicator;
3122 let mut task = self.code_actions_task.take();
3123 cx.spawn(|this, mut cx| async move {
3124 while let Some(prev_task) = task {
3125 prev_task.await;
3126 task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
3127 }
3128
3129 this.update(&mut cx, |this, cx| {
3130 if this.focused {
3131 if let Some((buffer, actions)) = this.available_code_actions.clone() {
3132 this.show_context_menu(
3133 ContextMenu::CodeActions(CodeActionsMenu {
3134 buffer,
3135 actions,
3136 selected_item: Default::default(),
3137 list: Default::default(),
3138 deployed_from_indicator,
3139 }),
3140 cx,
3141 );
3142 }
3143 }
3144 })?;
3145
3146 Ok::<_, anyhow::Error>(())
3147 })
3148 .detach_and_log_err(cx);
3149 }
3150
3151 pub fn confirm_code_action(
3152 workspace: &mut Workspace,
3153 action: &ConfirmCodeAction,
3154 cx: &mut ViewContext<Workspace>,
3155 ) -> Option<Task<Result<()>>> {
3156 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
3157 let actions_menu = if let ContextMenu::CodeActions(menu) =
3158 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
3159 {
3160 menu
3161 } else {
3162 return None;
3163 };
3164 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
3165 let action = actions_menu.actions.get(action_ix)?.clone();
3166 let title = action.lsp_action.title.clone();
3167 let buffer = actions_menu.buffer;
3168
3169 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
3170 project.apply_code_action(buffer, action, true, cx)
3171 });
3172 let editor = editor.downgrade();
3173 Some(cx.spawn(|workspace, cx| async move {
3174 let project_transaction = apply_code_actions.await?;
3175 Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
3176 }))
3177 }
3178
3179 async fn open_project_transaction(
3180 this: &WeakViewHandle<Editor>,
3181 workspace: WeakViewHandle<Workspace>,
3182 transaction: ProjectTransaction,
3183 title: String,
3184 mut cx: AsyncAppContext,
3185 ) -> Result<()> {
3186 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
3187
3188 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
3189 entries.sort_unstable_by_key(|(buffer, _)| {
3190 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
3191 });
3192
3193 // If the project transaction's edits are all contained within this editor, then
3194 // avoid opening a new editor to display them.
3195
3196 if let Some((buffer, transaction)) = entries.first() {
3197 if entries.len() == 1 {
3198 let excerpt = this.read_with(&cx, |editor, cx| {
3199 editor
3200 .buffer()
3201 .read(cx)
3202 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
3203 })?;
3204 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
3205 if excerpted_buffer == *buffer {
3206 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
3207 let excerpt_range = excerpt_range.to_offset(buffer);
3208 buffer
3209 .edited_ranges_for_transaction::<usize>(transaction)
3210 .all(|range| {
3211 excerpt_range.start <= range.start
3212 && excerpt_range.end >= range.end
3213 })
3214 });
3215
3216 if all_edits_within_excerpt {
3217 return Ok(());
3218 }
3219 }
3220 }
3221 }
3222 } else {
3223 return Ok(());
3224 }
3225
3226 let mut ranges_to_highlight = Vec::new();
3227 let excerpt_buffer = cx.add_model(|cx| {
3228 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
3229 for (buffer_handle, transaction) in &entries {
3230 let buffer = buffer_handle.read(cx);
3231 ranges_to_highlight.extend(
3232 multibuffer.push_excerpts_with_context_lines(
3233 buffer_handle.clone(),
3234 buffer
3235 .edited_ranges_for_transaction::<usize>(transaction)
3236 .collect(),
3237 1,
3238 cx,
3239 ),
3240 );
3241 }
3242 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
3243 multibuffer
3244 });
3245
3246 workspace.update(&mut cx, |workspace, cx| {
3247 let project = workspace.project().clone();
3248 let editor =
3249 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
3250 workspace.add_item(Box::new(editor.clone()), cx);
3251 editor.update(cx, |editor, cx| {
3252 editor.highlight_background::<Self>(
3253 ranges_to_highlight,
3254 |theme| theme.editor.highlighted_line_background,
3255 cx,
3256 );
3257 });
3258 })?;
3259
3260 Ok(())
3261 }
3262
3263 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3264 let project = self.project.as_ref()?;
3265 let buffer = self.buffer.read(cx);
3266 let newest_selection = self.selections.newest_anchor().clone();
3267 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
3268 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
3269 if start_buffer != end_buffer {
3270 return None;
3271 }
3272
3273 let actions = project.update(cx, |project, cx| {
3274 project.code_actions(&start_buffer, start..end, cx)
3275 });
3276 self.code_actions_task = Some(cx.spawn(|this, mut cx| async move {
3277 let actions = actions.await;
3278 this.update(&mut cx, |this, cx| {
3279 this.available_code_actions = actions.log_err().and_then(|actions| {
3280 if actions.is_empty() {
3281 None
3282 } else {
3283 Some((start_buffer, actions.into()))
3284 }
3285 });
3286 cx.notify();
3287 })
3288 .log_err();
3289 }));
3290 None
3291 }
3292
3293 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
3294 if self.pending_rename.is_some() {
3295 return None;
3296 }
3297
3298 let project = self.project.as_ref()?;
3299 let buffer = self.buffer.read(cx);
3300 let newest_selection = self.selections.newest_anchor().clone();
3301 let cursor_position = newest_selection.head();
3302 let (cursor_buffer, cursor_buffer_position) =
3303 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
3304 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
3305 if cursor_buffer != tail_buffer {
3306 return None;
3307 }
3308
3309 let highlights = project.update(cx, |project, cx| {
3310 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
3311 });
3312
3313 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
3314 if let Some(highlights) = highlights.await.log_err() {
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_trigger_point {
8309 if event.cmd && !pending_selection {
8310 let point = point.clone();
8311 let snapshot = self.snapshot(cx);
8312 let kind = point.definition_kind(event.shift);
8313
8314 show_link_definition(kind, self, point, snapshot, cx);
8315 return false;
8316 }
8317 }
8318
8319 {
8320 if self.link_go_to_definition_state.symbol_range.is_some()
8321 || !self.link_go_to_definition_state.definitions.is_empty()
8322 {
8323 self.link_go_to_definition_state.symbol_range.take();
8324 self.link_go_to_definition_state.definitions.clear();
8325 cx.notify();
8326 }
8327
8328 self.link_go_to_definition_state.task = None;
8329
8330 self.clear_text_highlights::<LinkGoToDefinitionState>(cx);
8331 }
8332
8333 false
8334 }
8335
8336 fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
8337 Self::reset_to_default_keymap_context(keymap);
8338 let mode = match self.mode {
8339 EditorMode::SingleLine => "single_line",
8340 EditorMode::AutoHeight { .. } => "auto_height",
8341 EditorMode::Full => "full",
8342 };
8343 keymap.add_key("mode", mode);
8344 if self.pending_rename.is_some() {
8345 keymap.add_identifier("renaming");
8346 }
8347 match self.context_menu.as_ref() {
8348 Some(ContextMenu::Completions(_)) => {
8349 keymap.add_identifier("menu");
8350 keymap.add_identifier("showing_completions")
8351 }
8352 Some(ContextMenu::CodeActions(_)) => {
8353 keymap.add_identifier("menu");
8354 keymap.add_identifier("showing_code_actions")
8355 }
8356 None => {}
8357 }
8358 for layer in self.keymap_context_layers.values() {
8359 keymap.extend(layer);
8360 }
8361
8362 if let Some(extension) = self
8363 .buffer
8364 .read(cx)
8365 .as_singleton()
8366 .and_then(|buffer| buffer.read(cx).file()?.path().extension()?.to_str())
8367 {
8368 keymap.add_key("extension", extension.to_string());
8369 }
8370 }
8371
8372 fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
8373 Some(
8374 self.buffer
8375 .read(cx)
8376 .read(cx)
8377 .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end))
8378 .collect(),
8379 )
8380 }
8381
8382 fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8383 // Prevent the IME menu from appearing when holding down an alphabetic key
8384 // while input is disabled.
8385 if !self.input_enabled {
8386 return None;
8387 }
8388
8389 let range = self.selections.newest::<OffsetUtf16>(cx).range();
8390 Some(range.start.0..range.end.0)
8391 }
8392
8393 fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
8394 let snapshot = self.buffer.read(cx).read(cx);
8395 let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
8396 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
8397 }
8398
8399 fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
8400 self.clear_text_highlights::<InputComposition>(cx);
8401 self.ime_transaction.take();
8402 }
8403
8404 fn replace_text_in_range(
8405 &mut self,
8406 range_utf16: Option<Range<usize>>,
8407 text: &str,
8408 cx: &mut ViewContext<Self>,
8409 ) {
8410 self.transact(cx, |this, cx| {
8411 if this.input_enabled {
8412 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
8413 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8414 Some(this.selection_replacement_ranges(range_utf16, cx))
8415 } else {
8416 this.marked_text_ranges(cx)
8417 };
8418
8419 if let Some(new_selected_ranges) = new_selected_ranges {
8420 this.change_selections(None, cx, |selections| {
8421 selections.select_ranges(new_selected_ranges)
8422 });
8423 }
8424 }
8425
8426 this.handle_input(text, cx);
8427 });
8428
8429 if !self.input_enabled {
8430 return;
8431 }
8432
8433 if let Some(transaction) = self.ime_transaction {
8434 self.buffer.update(cx, |buffer, cx| {
8435 buffer.group_until_transaction(transaction, cx);
8436 });
8437 }
8438
8439 self.unmark_text(cx);
8440 }
8441
8442 fn replace_and_mark_text_in_range(
8443 &mut self,
8444 range_utf16: Option<Range<usize>>,
8445 text: &str,
8446 new_selected_range_utf16: Option<Range<usize>>,
8447 cx: &mut ViewContext<Self>,
8448 ) {
8449 if !self.input_enabled {
8450 return;
8451 }
8452
8453 let transaction = self.transact(cx, |this, cx| {
8454 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
8455 let snapshot = this.buffer.read(cx).read(cx);
8456 if let Some(relative_range_utf16) = range_utf16.as_ref() {
8457 for marked_range in &mut marked_ranges {
8458 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
8459 marked_range.start.0 += relative_range_utf16.start;
8460 marked_range.start =
8461 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
8462 marked_range.end =
8463 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
8464 }
8465 }
8466 Some(marked_ranges)
8467 } else if let Some(range_utf16) = range_utf16 {
8468 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
8469 Some(this.selection_replacement_ranges(range_utf16, cx))
8470 } else {
8471 None
8472 };
8473
8474 if let Some(ranges) = ranges_to_replace {
8475 this.change_selections(None, cx, |s| s.select_ranges(ranges));
8476 }
8477
8478 let marked_ranges = {
8479 let snapshot = this.buffer.read(cx).read(cx);
8480 this.selections
8481 .disjoint_anchors()
8482 .iter()
8483 .map(|selection| {
8484 selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot)
8485 })
8486 .collect::<Vec<_>>()
8487 };
8488
8489 if text.is_empty() {
8490 this.unmark_text(cx);
8491 } else {
8492 this.highlight_text::<InputComposition>(
8493 marked_ranges.clone(),
8494 this.style(cx).composition_mark,
8495 cx,
8496 );
8497 }
8498
8499 this.handle_input(text, cx);
8500
8501 if let Some(new_selected_range) = new_selected_range_utf16 {
8502 let snapshot = this.buffer.read(cx).read(cx);
8503 let new_selected_ranges = marked_ranges
8504 .into_iter()
8505 .map(|marked_range| {
8506 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
8507 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
8508 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
8509 snapshot.clip_offset_utf16(new_start, Bias::Left)
8510 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
8511 })
8512 .collect::<Vec<_>>();
8513
8514 drop(snapshot);
8515 this.change_selections(None, cx, |selections| {
8516 selections.select_ranges(new_selected_ranges)
8517 });
8518 }
8519 });
8520
8521 self.ime_transaction = self.ime_transaction.or(transaction);
8522 if let Some(transaction) = self.ime_transaction {
8523 self.buffer.update(cx, |buffer, cx| {
8524 buffer.group_until_transaction(transaction, cx);
8525 });
8526 }
8527
8528 if self.text_highlights::<InputComposition>(cx).is_none() {
8529 self.ime_transaction.take();
8530 }
8531 }
8532}
8533
8534fn build_style(
8535 settings: &ThemeSettings,
8536 get_field_editor_theme: Option<&GetFieldEditorTheme>,
8537 override_text_style: Option<&OverrideTextStyle>,
8538 cx: &AppContext,
8539) -> EditorStyle {
8540 let font_cache = cx.font_cache();
8541 let line_height_scalar = settings.line_height();
8542 let theme_id = settings.theme.meta.id;
8543 let mut theme = settings.theme.editor.clone();
8544 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
8545 let field_editor_theme = get_field_editor_theme(&settings.theme);
8546 theme.text_color = field_editor_theme.text.color;
8547 theme.selection = field_editor_theme.selection;
8548 theme.background = field_editor_theme
8549 .container
8550 .background_color
8551 .unwrap_or_default();
8552 EditorStyle {
8553 text: field_editor_theme.text,
8554 placeholder_text: field_editor_theme.placeholder_text,
8555 line_height_scalar,
8556 theme,
8557 theme_id,
8558 }
8559 } else {
8560 let font_family_id = settings.buffer_font_family;
8561 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
8562 let font_properties = Default::default();
8563 let font_id = font_cache
8564 .select_font(font_family_id, &font_properties)
8565 .unwrap();
8566 let font_size = settings.buffer_font_size(cx);
8567 EditorStyle {
8568 text: TextStyle {
8569 color: settings.theme.editor.text_color,
8570 font_family_name,
8571 font_family_id,
8572 font_id,
8573 font_size,
8574 font_properties,
8575 underline: Default::default(),
8576 soft_wrap: false,
8577 },
8578 placeholder_text: None,
8579 line_height_scalar,
8580 theme,
8581 theme_id,
8582 }
8583 };
8584
8585 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
8586 if let Some(highlighted) = style
8587 .text
8588 .clone()
8589 .highlight(highlight_style, font_cache)
8590 .log_err()
8591 {
8592 style.text = highlighted;
8593 }
8594 }
8595
8596 style
8597}
8598
8599trait SelectionExt {
8600 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
8601 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
8602 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
8603 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
8604 -> Range<u32>;
8605}
8606
8607impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
8608 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
8609 let start = self.start.to_point(buffer);
8610 let end = self.end.to_point(buffer);
8611 if self.reversed {
8612 end..start
8613 } else {
8614 start..end
8615 }
8616 }
8617
8618 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
8619 let start = self.start.to_offset(buffer);
8620 let end = self.end.to_offset(buffer);
8621 if self.reversed {
8622 end..start
8623 } else {
8624 start..end
8625 }
8626 }
8627
8628 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
8629 let start = self
8630 .start
8631 .to_point(&map.buffer_snapshot)
8632 .to_display_point(map);
8633 let end = self
8634 .end
8635 .to_point(&map.buffer_snapshot)
8636 .to_display_point(map);
8637 if self.reversed {
8638 end..start
8639 } else {
8640 start..end
8641 }
8642 }
8643
8644 fn spanned_rows(
8645 &self,
8646 include_end_if_at_line_start: bool,
8647 map: &DisplaySnapshot,
8648 ) -> Range<u32> {
8649 let start = self.start.to_point(&map.buffer_snapshot);
8650 let mut end = self.end.to_point(&map.buffer_snapshot);
8651 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
8652 end.row -= 1;
8653 }
8654
8655 let buffer_start = map.prev_line_boundary(start).0;
8656 let buffer_end = map.next_line_boundary(end).0;
8657 buffer_start.row..buffer_end.row + 1
8658 }
8659}
8660
8661impl<T: InvalidationRegion> InvalidationStack<T> {
8662 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
8663 where
8664 S: Clone + ToOffset,
8665 {
8666 while let Some(region) = self.last() {
8667 let all_selections_inside_invalidation_ranges =
8668 if selections.len() == region.ranges().len() {
8669 selections
8670 .iter()
8671 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
8672 .all(|(selection, invalidation_range)| {
8673 let head = selection.head().to_offset(buffer);
8674 invalidation_range.start <= head && invalidation_range.end >= head
8675 })
8676 } else {
8677 false
8678 };
8679
8680 if all_selections_inside_invalidation_ranges {
8681 break;
8682 } else {
8683 self.pop();
8684 }
8685 }
8686 }
8687}
8688
8689impl<T> Default for InvalidationStack<T> {
8690 fn default() -> Self {
8691 Self(Default::default())
8692 }
8693}
8694
8695impl<T> Deref for InvalidationStack<T> {
8696 type Target = Vec<T>;
8697
8698 fn deref(&self) -> &Self::Target {
8699 &self.0
8700 }
8701}
8702
8703impl<T> DerefMut for InvalidationStack<T> {
8704 fn deref_mut(&mut self) -> &mut Self::Target {
8705 &mut self.0
8706 }
8707}
8708
8709impl InvalidationRegion for SnippetState {
8710 fn ranges(&self) -> &[Range<Anchor>] {
8711 &self.ranges[self.active_index]
8712 }
8713}
8714
8715impl Deref for EditorStyle {
8716 type Target = theme::Editor;
8717
8718 fn deref(&self) -> &Self::Target {
8719 &self.theme
8720 }
8721}
8722
8723pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
8724 let mut highlighted_lines = Vec::new();
8725
8726 for (index, line) in diagnostic.message.lines().enumerate() {
8727 let line = match &diagnostic.source {
8728 Some(source) if index == 0 => {
8729 let source_highlight = Vec::from_iter(0..source.len());
8730 highlight_diagnostic_message(source_highlight, &format!("{source}: {line}"))
8731 }
8732
8733 _ => highlight_diagnostic_message(Vec::new(), line),
8734 };
8735 highlighted_lines.push(line);
8736 }
8737 let message = diagnostic.message;
8738 Arc::new(move |cx: &mut BlockContext| {
8739 let message = message.clone();
8740 let settings = settings::get::<ThemeSettings>(cx);
8741 let tooltip_style = settings.theme.tooltip.clone();
8742 let theme = &settings.theme.editor;
8743 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
8744 let font_size = (style.text_scale_factor * settings.buffer_font_size(cx)).round();
8745 let anchor_x = cx.anchor_x;
8746 enum BlockContextToolip {}
8747 MouseEventHandler::new::<BlockContext, _>(cx.block_id, cx, |_, _| {
8748 Flex::column()
8749 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
8750 Label::new(
8751 line.clone(),
8752 style.message.clone().with_font_size(font_size),
8753 )
8754 .with_highlights(highlights.clone())
8755 .contained()
8756 .with_margin_left(anchor_x)
8757 }))
8758 .aligned()
8759 .left()
8760 .into_any()
8761 })
8762 .with_cursor_style(CursorStyle::PointingHand)
8763 .on_click(MouseButton::Left, move |_, _, cx| {
8764 cx.write_to_clipboard(ClipboardItem::new(message.clone()));
8765 })
8766 // We really need to rethink this ID system...
8767 .with_tooltip::<BlockContextToolip>(
8768 cx.block_id,
8769 "Copy diagnostic message",
8770 None,
8771 tooltip_style,
8772 cx,
8773 )
8774 .into_any()
8775 })
8776}
8777
8778pub fn highlight_diagnostic_message(
8779 initial_highlights: Vec<usize>,
8780 message: &str,
8781) -> (String, Vec<usize>) {
8782 let mut message_without_backticks = String::new();
8783 let mut prev_offset = 0;
8784 let mut inside_block = false;
8785 let mut highlights = initial_highlights;
8786 for (match_ix, (offset, _)) in message
8787 .match_indices('`')
8788 .chain([(message.len(), "")])
8789 .enumerate()
8790 {
8791 message_without_backticks.push_str(&message[prev_offset..offset]);
8792 if inside_block {
8793 highlights.extend(prev_offset - match_ix..offset - match_ix);
8794 }
8795
8796 inside_block = !inside_block;
8797 prev_offset = offset + 1;
8798 }
8799
8800 (message_without_backticks, highlights)
8801}
8802
8803pub fn diagnostic_style(
8804 severity: DiagnosticSeverity,
8805 valid: bool,
8806 theme: &theme::Editor,
8807) -> DiagnosticStyle {
8808 match (severity, valid) {
8809 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
8810 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
8811 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
8812 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
8813 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
8814 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
8815 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
8816 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
8817 _ => theme.invalid_hint_diagnostic.clone(),
8818 }
8819}
8820
8821pub fn combine_syntax_and_fuzzy_match_highlights(
8822 text: &str,
8823 default_style: HighlightStyle,
8824 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
8825 match_indices: &[usize],
8826) -> Vec<(Range<usize>, HighlightStyle)> {
8827 let mut result = Vec::new();
8828 let mut match_indices = match_indices.iter().copied().peekable();
8829
8830 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
8831 {
8832 syntax_highlight.weight = None;
8833
8834 // Add highlights for any fuzzy match characters before the next
8835 // syntax highlight range.
8836 while let Some(&match_index) = match_indices.peek() {
8837 if match_index >= range.start {
8838 break;
8839 }
8840 match_indices.next();
8841 let end_index = char_ix_after(match_index, text);
8842 let mut match_style = default_style;
8843 match_style.weight = Some(fonts::Weight::BOLD);
8844 result.push((match_index..end_index, match_style));
8845 }
8846
8847 if range.start == usize::MAX {
8848 break;
8849 }
8850
8851 // Add highlights for any fuzzy match characters within the
8852 // syntax highlight range.
8853 let mut offset = range.start;
8854 while let Some(&match_index) = match_indices.peek() {
8855 if match_index >= range.end {
8856 break;
8857 }
8858
8859 match_indices.next();
8860 if match_index > offset {
8861 result.push((offset..match_index, syntax_highlight));
8862 }
8863
8864 let mut end_index = char_ix_after(match_index, text);
8865 while let Some(&next_match_index) = match_indices.peek() {
8866 if next_match_index == end_index && next_match_index < range.end {
8867 end_index = char_ix_after(next_match_index, text);
8868 match_indices.next();
8869 } else {
8870 break;
8871 }
8872 }
8873
8874 let mut match_style = syntax_highlight;
8875 match_style.weight = Some(fonts::Weight::BOLD);
8876 result.push((match_index..end_index, match_style));
8877 offset = end_index;
8878 }
8879
8880 if offset < range.end {
8881 result.push((offset..range.end, syntax_highlight));
8882 }
8883 }
8884
8885 fn char_ix_after(ix: usize, text: &str) -> usize {
8886 ix + text[ix..].chars().next().unwrap().len_utf8()
8887 }
8888
8889 result
8890}
8891
8892pub fn styled_runs_for_code_label<'a>(
8893 label: &'a CodeLabel,
8894 syntax_theme: &'a theme::SyntaxTheme,
8895) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
8896 let fade_out = HighlightStyle {
8897 fade_out: Some(0.35),
8898 ..Default::default()
8899 };
8900
8901 let mut prev_end = label.filter_range.end;
8902 label
8903 .runs
8904 .iter()
8905 .enumerate()
8906 .flat_map(move |(ix, (range, highlight_id))| {
8907 let style = if let Some(style) = highlight_id.style(syntax_theme) {
8908 style
8909 } else {
8910 return Default::default();
8911 };
8912 let mut muted_style = style;
8913 muted_style.highlight(fade_out);
8914
8915 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
8916 if range.start >= label.filter_range.end {
8917 if range.start > prev_end {
8918 runs.push((prev_end..range.start, fade_out));
8919 }
8920 runs.push((range.clone(), muted_style));
8921 } else if range.end <= label.filter_range.end {
8922 runs.push((range.clone(), style));
8923 } else {
8924 runs.push((range.start..label.filter_range.end, style));
8925 runs.push((label.filter_range.end..range.end, muted_style));
8926 }
8927 prev_end = cmp::max(prev_end, range.end);
8928
8929 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
8930 runs.push((prev_end..label.text.len(), fade_out));
8931 }
8932
8933 runs
8934 })
8935}
8936
8937pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
8938 let mut index = 0;
8939 let mut codepoints = text.char_indices().peekable();
8940
8941 std::iter::from_fn(move || {
8942 let start_index = index;
8943 while let Some((new_index, codepoint)) = codepoints.next() {
8944 index = new_index + codepoint.len_utf8();
8945 let current_upper = codepoint.is_uppercase();
8946 let next_upper = codepoints
8947 .peek()
8948 .map(|(_, c)| c.is_uppercase())
8949 .unwrap_or(false);
8950
8951 if !current_upper && next_upper {
8952 return Some(&text[start_index..index]);
8953 }
8954 }
8955
8956 index = text.len();
8957 if start_index < text.len() {
8958 return Some(&text[start_index..]);
8959 }
8960 None
8961 })
8962 .flat_map(|word| word.split_inclusive('_'))
8963}
8964
8965trait RangeToAnchorExt {
8966 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
8967}
8968
8969impl<T: ToOffset> RangeToAnchorExt for Range<T> {
8970 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
8971 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
8972 }
8973}