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