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