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