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