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