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