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