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