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