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