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